diff --git a/Makefile b/Makefile index 6b085447..a4456456 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ PROGS = ghcli LIBS = libghcli.a -GHCLI_VERSION = 0.7.2-alpha +GHCLI_VERSION = 0.7.3-alpha # These and LDFLAGS can be overwritten CFLAGS = -std=iso9899:1999 \ -Ithirdparty/pdjson/ \ diff --git a/docs/ghcli.1 b/docs/ghcli.1 index fef1a56f..fff7cb86 100644 --- a/docs/ghcli.1 +++ b/docs/ghcli.1 @@ -106,6 +106,18 @@ Do not output ANSI escape sequences for changing text formatting. Default is to output colors unless stdout is not a tty. See .Xr isatty 3 . +.It Fl q , -quiet +Suppress warning messages about missing config files and various other +things. +.It Fl t , -type Ar forge-type +Forcefully override the forge type if it can't be inferred +correctly. Set +.Ar forge-type +to +.Sq github +or +.Sq gitlab +to connect to the corresponding services. .El Common options across almost all of the subcommands are: @@ -237,6 +249,13 @@ Get a summary and comments of upstream PR #22: .Bd -literal -width indent $ ghcli pulls -p 22 summary comments .Ed + +List the last 10 issues in contour-terminal/contour ignoring all +warnings and forcing a connection to GitHub. +.Bd -literal -width indent +ghcli -t github -q issues -o contour-terminal -r contour -a -n10 +.Ed +This works when you don't have a config file in place. .Sh SEE ALSO .Xr git 1 , .Xr ghcli-issues 1 , diff --git a/src/config.c b/src/config.c index d5802452..b44ddf73 100644 --- a/src/config.c +++ b/src/config.c @@ -55,6 +55,7 @@ static struct ghcli_config { const char *override_default_account; const char *override_remote; + int override_forgetype; int colors_disabled; sn_sv buffer; @@ -182,7 +183,7 @@ init_local_config(void) if (line.length == 0) errx(1, "%s:%d: Unexpected end of line", - path, curr_line); + path, curr_line); // Comments if (line.data[0] == '#') { @@ -272,7 +273,7 @@ parse_section_title(struct config_parser *input) size_t len = 0; if (input->buffer.length == 0) errx(1, "%s:%d: unexpected end of input in section title", - input->filename, input->line); + input->filename, input->line); while (!isspace(input->buffer.data[len]) && input->buffer.data[len] != '{') @@ -314,7 +315,7 @@ parse_config_section(struct config_parser *input) if (section->entries_size == CONFIG_MAX_ENTRIES) errx(1, "error: too many config entries in section "SV_FMT, - SV_ARGS(section->title)); + SV_ARGS(section->title)); parse_keyvaluepair(input, §ion->entries[section->entries_size++]); skip_ws_and_comments(input); @@ -322,7 +323,7 @@ parse_config_section(struct config_parser *input) if (input->buffer.length == 0) errx(1, "%s:%d: missing '}' before end of file", - input->filename, input->line); + input->filename, input->line); input->buffer.length -= 1; input->buffer.data += 1; @@ -339,22 +340,28 @@ parse_config_file(struct config_parser *input) } } -static void +/** + * Try to load up the local config file if it exists. If we succeed, + * return 0. Otherwise return -1. + */ +static int ensure_config(void) { char *file_path = NULL; struct config_parser parser = {0}; if (config.inited) - return; + return 0; config.inited = true; file_path = getenv("XDG_CONFIG_PATH"); if (!file_path) { file_path = getenv("HOME"); - if (!file_path) - errx(1, "Neither XDG_CONFIG_PATH nor HOME set in env"); + if (!file_path) { + warnx("Neither XDG_CONFIG_PATH nor HOME set in env"); + return -1; + } /* * Code duplication to avoid leaking pointers */ @@ -363,8 +370,10 @@ ensure_config(void) file_path = sn_asprintf("%s/ghcli/config", file_path); } - if (access(file_path, R_OK) < 0) - err(1, "Cannot access config file at %s", file_path); + if (access(file_path, R_OK) < 0) { + warn("Cannot access config file at %s", file_path); + return -1; + } int len = sn_mmap_file(file_path, &config.mmap_pointer); if (len < 0) @@ -380,6 +389,8 @@ ensure_config(void) parse_config_file(&parser); free((void *)file_path); + + return 0; } void @@ -404,10 +415,22 @@ ghcli_config_init(int *argc, char ***argv) .has_arg = no_argument, .flag = &config.colors_disabled, .val = 1 }, + { .name = "type", + .has_arg = required_argument, + .flag = NULL, + .val = 't' }, + { .name = "quiet", + .has_arg = no_argument, + .flag = NULL, + .val = 'q' }, {0}, }; - while ((ch = getopt_long(*argc, *argv, "+a:r:c", options, NULL)) != -1) { + /* Before we parse options, invalidate the override type so it + * doesn't get confused later */ + config.override_forgetype = -1; + + while ((ch = getopt_long(*argc, *argv, "+a:r:cqt:", options, NULL)) != -1) { switch (ch) { case 'a': { config.override_default_account = optarg; @@ -418,6 +441,18 @@ ghcli_config_init(int *argc, char ***argv) case 'c': { config.colors_disabled = 1; } break; + case 'q': { + sn_setquiet(1); + } break; + case 't': { + if (strcmp(optarg, "github") == 0) + config.override_forgetype = GHCLI_FORGE_GITHUB; + else if (strcmp(optarg, "gitlab") == 0) + config.override_forgetype = GHCLI_FORGE_GITLAB; + else + errx(1, "error: unknown forge type '%s'. " + "Have either github or gitlab.", optarg); + } break; case 0: break; case '?': default: @@ -544,6 +579,10 @@ ghcli_config_get_override_default_account(void) ghcli_forge_type ghcli_config_get_forge_type(void) { + /* Hard override */ + if (config.override_forgetype >= 0) + return config.override_forgetype; + ensure_config(); init_local_config(); @@ -554,8 +593,8 @@ ghcli_config_get_forge_type(void) entry = ghcli_config_find_by_key(section, "forge-type"); if (sn_sv_null(entry)) errx(1, - "error: given default override account not found or " - "missing forge-type"); + "error: given default override account not found or " + "missing forge-type"); } else { entry = ghcli_local_config_find_by_key("forge-type"); } @@ -569,7 +608,14 @@ ghcli_config_get_forge_type(void) errx(1, "Unknown forge type "SV_FMT, SV_ARGS(entry)); } - return ghcli_gitconfig_get_forgetype(config.override_remote); + /* As a last resort, try to infer from the git remote */ + int type = ghcli_gitconfig_get_forgetype(config.override_remote); + if (type < 0) + errx(1, "error: cannot infer forge type. " + "use -t to overrride manually."); + + return type; + } void diff --git a/src/curl.c b/src/curl.c index f547721d..21c1de58 100644 --- a/src/curl.c +++ b/src/curl.c @@ -79,19 +79,19 @@ ghcli_curl_check_api_error( if (code != CURLE_OK) errx(1, - "error: request to %s failed\n" - " : curl error: %s", - url, - curl_easy_strerror(code)); + "error: request to %s failed\n" + " : curl error: %s", + url, + curl_easy_strerror(code)); curl_easy_getinfo(ghcli_curl_session, CURLINFO_RESPONSE_CODE, &status_code); if (status_code >= 300L) { errx(1, - "error: request to %s failed with code %ld\n" - " : API error: %s", - url, status_code, - ghcli_forge()->get_api_error_string(result)); + "error: request to %s failed with code %ld\n" + " : API error: %s", + url, status_code, + ghcli_forge()->get_api_error_string(result)); } } @@ -304,7 +304,8 @@ ghcli_fetch_with_method( headers = curl_slist_append( headers, "Content-Type: application/json"); - headers = curl_slist_append(headers, auth_header); + if (auth_header) + headers = curl_slist_append(headers, auth_header); *out = (ghcli_fetch_buffer) {0}; diff --git a/src/forges.c b/src/forges.c index 492ae62c..1c77202e 100644 --- a/src/forges.c +++ b/src/forges.c @@ -163,8 +163,8 @@ ghcli_forge(void) return &gitlab_forge_descriptor; default: errx(1, - "error: cannot determine forge type. try forcing an account " - "with -a or create a .ghcli file."); + "error: cannot determine forge type. try forcing an account " + "with -a, specifying -t or create a .ghcli file."); } return NULL; } diff --git a/src/gitconfig.c b/src/gitconfig.c index 638777b0..ba23b833 100644 --- a/src/gitconfig.c +++ b/src/gitconfig.c @@ -102,7 +102,8 @@ find_file_in_dotgit(const char *fname) if (strcmp("/", curr_dir_path) == 0) { free(curr_dir_path); closedir(curr_dir); - errx(1, "error: not a git repository"); + warn("not a git repository"); + return NULL; } } @@ -192,7 +193,7 @@ http_extractor(ghcli_gitremote *remote, const char *prefix) remote->forge_type = GHCLI_FORGE_GITLAB; } else { warnx("non-github or non-gitlab https remotes are not supported " - "and will likely cause bugs"); + "and will likely cause bugs"); } pair.length -= prefix_size; @@ -310,7 +311,10 @@ ghcli_gitconfig_read_gitconfig(void) const char *path = NULL; sn_sv buffer = {0}; - path = ghcli_find_gitconfig(); + path = ghcli_find_gitconfig(); + if (!path) + return; + buffer.length = sn_mmap_file(path, (void **)&buffer.data); while (buffer.length > 0) { @@ -359,7 +363,7 @@ ghcli_gitconfig_add_fork_remote(const char *org, const char *repo) if ((pid = fork()) == 0) { printf("[INFO] git remote rename origin upstream\n"); execlp("git", "git", "remote", - "rename", "origin", "upstream", NULL); + "rename", "origin", "upstream", NULL); } else if (pid > 0) { int status = 0; waitpid(pid, &status, 0); @@ -412,8 +416,10 @@ ghcli_gitconfig_get_forgetype(const char *remote_name) } } - if (!remotes_size) - errx(1, "error: no remotes to auto-detect forge"); + if (!remotes_size) { + warn("no remotes to auto-detect forge"); + return -1; + } return remotes[0].forge_type; } diff --git a/src/github/config.c b/src/github/config.c index b251004c..e9b4f27e 100644 --- a/src/github/config.c +++ b/src/github/config.c @@ -44,7 +44,7 @@ github_default_account_name(void) "github-default-account"); if (sn_sv_null(section_name)) - errx(1, "Config file does not name a default GitHub account name."); + warnx("Config file does not name a default GitHub account name."); } return section_name; @@ -54,18 +54,27 @@ char * github_get_apibase(void) { sn_sv account_name = github_default_account_name(); + if (sn_sv_null(account_name)) + goto default_val; + sn_sv api_base = ghcli_config_find_by_key(account_name, "apibase"); if (sn_sv_null(api_base)) - return "https://api.github.com"; + goto default_val; return sn_sv_to_cstr(api_base); + +default_val: + return "https://api.github.com"; } char * github_get_authheader(void) { sn_sv account = github_default_account_name(); + if (sn_sv_null(account)) + return NULL; + sn_sv token = ghcli_config_find_by_key(account, "token");; if (sn_sv_null(token)) errx(1, "Missing Github token"); @@ -76,6 +85,9 @@ sn_sv github_get_account(void) { sn_sv section = github_default_account_name(); + if (sn_sv_null(section)) + return SV_NULL; + sn_sv account = ghcli_config_find_by_key(section, "account");; if (!account.length) errx(1, "Missing Github account name"); diff --git a/src/gitlab/config.c b/src/gitlab/config.c index 1f278d4a..a25f6722 100644 --- a/src/gitlab/config.c +++ b/src/gitlab/config.c @@ -48,7 +48,7 @@ gitlab_default_account_name(void) /* Welp, no luck here */ if (sn_sv_null(section_name)) - errx(1, "Config file does not name a default GitLab account name."); + warnx("Config file does not name a default GitLab account name."); } return section_name; @@ -57,19 +57,27 @@ gitlab_default_account_name(void) char * gitlab_get_apibase(void) { - sn_sv account = gitlab_default_account_name(); - sn_sv api_base = ghcli_config_find_by_key(account, "apibase"); + sn_sv account = gitlab_default_account_name(); + if (sn_sv_null(account)) + goto default_val; + sn_sv api_base = ghcli_config_find_by_key(account, "apibase"); if (sn_sv_null(api_base)) - return "https://gitlab.com/api/v4"; + goto default_val; return sn_sv_to_cstr(api_base); + +default_val: + return "https://gitlab.com/api/v4"; } char * gitlab_get_authheader(void) { sn_sv account = gitlab_default_account_name(); + if (sn_sv_null(account)) + return NULL; + sn_sv token = ghcli_config_find_by_key(account, "token"); if (sn_sv_null(token)) errx(1, "Missing GitLab token"); @@ -80,6 +88,9 @@ sn_sv gitlab_get_account(void) { sn_sv section = gitlab_default_account_name(); + if (sn_sv_null(section)) + return SV_NULL; + sn_sv account = ghcli_config_find_by_key(section, "account");; if (sn_sv_null(account)) errx(1, "Missing GitLab account name"); diff --git a/thirdparty/sn/sn.c b/thirdparty/sn/sn.c index afc55b65..8134bb04 100644 --- a/thirdparty/sn/sn.c +++ b/thirdparty/sn/sn.c @@ -44,372 +44,406 @@ #include +static int quiet_mode = 0; + +void +sn_setquiet(int quiet) +{ + quiet_mode = quiet; +} + +int +sn_getquiet(void) +{ + return quiet_mode; +} + + void errx(int code, const char *fmt, ...) { - va_list ap; + va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); - fputc('\n', stderr); - exit(code); + fputc('\n', stderr); + exit(code); } void err(int code, const char *fmt, ...) { - va_list ap; + va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); - fprintf(stderr, ": %s\n", strerror(errno)); - exit(code); + fprintf(stderr, ": %s\n", strerror(errno)); + exit(code); } void warnx(const char *fmt, ...) { - fputs("warning: ", stderr); - va_list ap; + if (quiet_mode) + return; + + fputs("warning: ", stderr); + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fputc('\n', stderr); +} + +void +warn(const char *fmt, ...) +{ + if (quiet_mode) + return; + + fputs("warning: ", stderr); + va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); - fputc('\n', stderr); + fprintf(stderr, ": %s\n", strerror(errno)); } char * sn_strndup(const char *it, size_t len) { - size_t actual = 0; - const char *tmp = NULL; - char *result = NULL; + size_t actual = 0; + const char *tmp = NULL; + char *result = NULL; - if (!len) - return NULL; + if (!len) + return NULL; - tmp = it; + tmp = it; - while (tmp[actual++] && actual < len); + while (tmp[actual++] && actual < len); - result = calloc(1, actual + 1); - memcpy(result, it, actual); - return result; + result = calloc(1, actual + 1); + memcpy(result, it, actual); + return result; } char * sn_asprintf(const char *fmt, ...) { - char tmp = 0, *result = NULL; - size_t actual = 0; - va_list vp; + char tmp = 0, *result = NULL; + size_t actual = 0; + va_list vp; - va_start(vp, fmt); - actual = vsnprintf(&tmp, 1, fmt, vp); - va_end(vp); + va_start(vp, fmt); + actual = vsnprintf(&tmp, 1, fmt, vp); + va_end(vp); - result = calloc(1, actual + 1); - if (!result) - err(1, "calloc"); + result = calloc(1, actual + 1); + if (!result) + err(1, "calloc"); - va_start(vp, fmt); - vsnprintf(result, actual + 1, fmt, vp); - va_end(vp); + va_start(vp, fmt); + vsnprintf(result, actual + 1, fmt, vp); + va_end(vp); - return result; + return result; } static int word_length(const char *x) { - int l = 0; + int l = 0; - while (*x && !isspace(*x++)) - l++; - return l; + while (*x && !isspace(*x++)) + l++; + return l; } void pretty_print(const char *input, int indent, int maxlinelen, FILE *out) { - const char *it = input; + const char *it = input; - if (!it) - return; + if (!it) + return; - while (*it) { - int linelength = indent; - fprintf(out, "%*.*s", indent, indent, ""); + while (*it) { + int linelength = indent; + fprintf(out, "%*.*s", indent, indent, ""); - do { - int w = word_length(it) + 1; + do { + int w = word_length(it) + 1; - if (it[w - 1] == '\n') { - fprintf(out, "%.*s", w - 1, it); - it += w; - break; - } + if (it[w - 1] == '\n') { + fprintf(out, "%.*s", w - 1, it); + it += w; + break; + } - fprintf(out, "%.*s", w, it); - it += w; - linelength += w; + fprintf(out, "%.*s", w, it); + it += w; + linelength += w; - } while (*it && (linelength < maxlinelen)); - fputc('\n', out); - } + } while (*it && (linelength < maxlinelen)); + fputc('\n', out); + } } int sn_mmap_file(const char *path, void **buffer) { - struct stat stat_buf = {0}; - int fd = 0; + struct stat stat_buf = {0}; + int fd = 0; - /* Precautiously nullify the buffer, because it is better to have - * it point to null if the pointer variable passed here is - * allocated on the stack and not initialized. This will make - * debugging easier. */ - *buffer = NULL; + /* Precautiously nullify the buffer, because it is better to have + * it point to null if the pointer variable passed here is + * allocated on the stack and not initialized. This will make + * debugging easier. */ + *buffer = NULL; - if (access(path, R_OK) < 0) - err(1, "access"); + if (access(path, R_OK) < 0) + err(1, "access"); - if (stat(path, &stat_buf) < 0) - err(1, "stat"); + if (stat(path, &stat_buf) < 0) + err(1, "stat"); - /* we should not pass a size of 0 to mmap, as this will trigger an - * EINVAL. Thus we can also avoid calling open on the file and - * save a few resources. I discovered this error the hard way in a - * Haiku VM. */ - if (stat_buf.st_size == 0) - return 0; + /* we should not pass a size of 0 to mmap, as this will trigger an + * EINVAL. Thus we can also avoid calling open on the file and + * save a few resources. I discovered this error the hard way in a + * Haiku VM. */ + if (stat_buf.st_size == 0) + return 0; - if ((fd = open(path, O_RDONLY)) < 0) - err(1, "open"); + if ((fd = open(path, O_RDONLY)) < 0) + err(1, "open"); - *buffer = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (*buffer == MAP_FAILED) - err(1, "mmap"); + *buffer = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (*buffer == MAP_FAILED) + err(1, "mmap"); - return stat_buf.st_size; + return stat_buf.st_size; } sn_sv sn_sv_trim_front(sn_sv it) { - if (it.length == 0) - return it; + if (it.length == 0) + return it; - // TODO: not utf-8 aware - while (it.length > 0) { - if (!isspace(*it.data)) - break; + // TODO: not utf-8 aware + while (it.length > 0) { + if (!isspace(*it.data)) + break; - it.data++; - it.length--; - } + it.data++; + it.length--; + } - return it; + return it; } static sn_sv sn_sv_trim_end(sn_sv it) { - while (it.length > 0 && isspace(it.data[it.length - 1])) - it.length--; + while (it.length > 0 && isspace(it.data[it.length - 1])) + it.length--; - return it; + return it; } sn_sv sn_sv_trim(sn_sv it) { - return sn_sv_trim_front(sn_sv_trim_end(it)); + return sn_sv_trim_front(sn_sv_trim_end(it)); } sn_sv sn_sv_chop_until(sn_sv *it, char c) { - sn_sv result = *it; + sn_sv result = *it; - result.length = 0; + result.length = 0; - while (it->length > 0) { + while (it->length > 0) { - if (*it->data == c) - break; + if (*it->data == c) + break; - it->data++; - it->length--; - result.length++; - } + it->data++; + it->length--; + result.length++; + } - return result; + return result; } bool sn_sv_has_prefix(sn_sv it, const char *prefix) { - size_t len = strlen(prefix); + size_t len = strlen(prefix); - if (it.length < len) - return false; + if (it.length < len) + return false; - return strncmp(it.data, prefix, len) == 0; + return strncmp(it.data, prefix, len) == 0; } bool sn_sv_eq(sn_sv this, sn_sv that) { - if (this.length != that.length) - return false; + if (this.length != that.length) + return false; - return strncmp(this.data, that.data, this.length) == 0; + return strncmp(this.data, that.data, this.length) == 0; } bool sn_sv_eq_to(const sn_sv this, const char *that) { - size_t len = strlen(that); - if (len != this.length) - return false; + size_t len = strlen(that); + if (len != this.length) + return false; - return strncmp(this.data, that, len) == 0; + return strncmp(this.data, that, len) == 0; } char * sn_strip_suffix(char *it, const char *suffix) { - int it_len = strlen(it); - int su_len = strlen(suffix); + int it_len = strlen(it); + int su_len = strlen(suffix); - if (su_len > it_len) - return it; + if (su_len > it_len) + return it; - int off = it_len - su_len; + int off = it_len - su_len; - if (strncmp(it + off, suffix, su_len) == 0) - it[off] = '\0'; + if (strncmp(it + off, suffix, su_len) == 0) + it[off] = '\0'; - return it; + return it; } sn_sv sn_sv_fmt(const char *fmt, ...) { - char tmp = 0; - va_list vp; - sn_sv result = {0}; + char tmp = 0; + va_list vp; + sn_sv result = {0}; - va_start(vp, fmt); + va_start(vp, fmt); - result.length = vsnprintf(&tmp, 1, fmt, vp); - va_end(vp); - result.data = calloc(1, result.length + 1); + result.length = vsnprintf(&tmp, 1, fmt, vp); + va_end(vp); + result.data = calloc(1, result.length + 1); - va_start(vp, fmt); - vsnprintf(result.data, result.length + 1, fmt, vp); - va_end(vp); + va_start(vp, fmt); + vsnprintf(result.data, result.length + 1, fmt, vp); + va_end(vp); - return result; + return result; } char * sn_sv_to_cstr(sn_sv it) { - return sn_strndup(it.data, it.length); + return sn_strndup(it.data, it.length); } bool sn_yesno(const char *fmt, ...) { - char tmp = 0; - va_list vp; - sn_sv message = {0}; - bool result = false; + char tmp = 0; + va_list vp; + sn_sv message = {0}; + bool result = false; - va_start(vp, fmt); + va_start(vp, fmt); - message.length = vsnprintf(&tmp, 1, fmt, vp); - va_end(vp); - message.data = calloc(1, message.length + 1); + message.length = vsnprintf(&tmp, 1, fmt, vp); + va_end(vp); + message.data = calloc(1, message.length + 1); - va_start(vp, fmt); - vsnprintf(message.data, message.length + 1, fmt, vp); - va_end(vp); + va_start(vp, fmt); + vsnprintf(message.data, message.length + 1, fmt, vp); + va_end(vp); - do { - printf(SV_FMT" [yN] ", SV_ARGS(message)); + do { + printf(SV_FMT" [yN] ", SV_ARGS(message)); - char c = getchar(); + char c = getchar(); - if (c == 'y' || c == 'Y') { - result = true; - break; - } else if (c == '\n' || c == 'n' || c == 'N') { - break; - } + if (c == 'y' || c == 'Y') { + result = true; + break; + } else if (c == '\n' || c == 'n' || c == 'N') { + break; + } - getchar(); // consume newline character + getchar(); // consume newline character - } while (!feof(stdin)); + } while (!feof(stdin)); - free(message.data); - return result; + free(message.data); + return result; } sn_sv sn_sv_strip_suffix(sn_sv input, const char *suffix) { - sn_sv expected_suffix = SV((char *)suffix); + sn_sv expected_suffix = SV((char *)suffix); - if (input.length < expected_suffix.length) - return input; + if (input.length < expected_suffix.length) + return input; - sn_sv actual_suffix = sn_sv_from_parts( - input.data + input.length - expected_suffix.length, - expected_suffix.length); + sn_sv actual_suffix = sn_sv_from_parts( + input.data + input.length - expected_suffix.length, + expected_suffix.length); - if (sn_sv_eq(expected_suffix, actual_suffix)) - input.length -= expected_suffix.length; + if (sn_sv_eq(expected_suffix, actual_suffix)) + input.length -= expected_suffix.length; - return input; + return input; } char * sn_join_with(const char *items[], size_t items_size, const char *sep) { - char *buffer = NULL; - size_t buffer_size = 0; - size_t bufoff = 0; - size_t sep_size = 0; + char *buffer = NULL; + size_t buffer_size = 0; + size_t bufoff = 0; + size_t sep_size = 0; - sep_size = strlen(sep); + sep_size = strlen(sep); - /* this works because of the null terminator at the end */ - for (size_t i = 0; i < items_size; ++i) { - buffer_size += strlen(items[i]) + sep_size; - } + /* this works because of the null terminator at the end */ + for (size_t i = 0; i < items_size; ++i) { + buffer_size += strlen(items[i]) + sep_size; + } - buffer = calloc(1, buffer_size); - if (!buffer) - return NULL; + buffer = calloc(1, buffer_size); + if (!buffer) + return NULL; - for (size_t i = 0; i < items_size; ++i) { - size_t len = strlen(items[i]); + for (size_t i = 0; i < items_size; ++i) { + size_t len = strlen(items[i]); - memcpy(buffer + bufoff, items[i], len); - if (i != items_size - 1) - memcpy(&buffer[bufoff + len], sep, sep_size); + memcpy(buffer + bufoff, items[i], len); + if (i != items_size - 1) + memcpy(&buffer[bufoff + len], sep, sep_size); - bufoff += len + sep_size; - } + bufoff += len + sep_size; + } - return buffer; + return buffer; } diff --git a/thirdparty/sn/sn.h b/thirdparty/sn/sn.h index 78533f6c..f26c7fa1 100644 --- a/thirdparty/sn/sn.h +++ b/thirdparty/sn/sn.h @@ -45,12 +45,17 @@ #define PRINTF_FORMAT(STRING_INDEX, FIRST_TO_CHECK) #endif +/* Make warn and warnx silent if quiet = 1 */ +void sn_setquiet(int quiet); +int sn_getquiet(void); + /* error functions */ /* print a formatted error message and exit with code */ void errx(int code, const char *fmt, ...) PRINTF_FORMAT(2, 3); /* print a formatted error message, the error retrieved from errno and exit with code */ void err(int code, const char *fmt, ...) PRINTF_FORMAT(2, 3); void warnx(const char *fmt, ...) PRINTF_FORMAT(1, 2); +void warn(const char *fmt, ...) PRINTF_FORMAT(1, 2); /* for convenience */ #define sn_unimplemented errx(42, "%s: unimplemented", __func__) @@ -72,8 +77,8 @@ int sn_mmap_file(const char *path, void **buffer); typedef struct sn_sv sn_sv; struct sn_sv { - char *data; - size_t length; + char *data; + size_t length; }; #define SV(x) (sn_sv) { .data = x, .length = strlen(x) } @@ -84,7 +89,7 @@ struct sn_sv { static inline sn_sv sn_sv_from_parts(char *buf, size_t len) { - return (sn_sv) { .data = buf, .length = len }; + return (sn_sv) { .data = buf, .length = len }; } sn_sv sn_sv_trim_front(sn_sv); @@ -100,7 +105,7 @@ sn_sv sn_sv_strip_suffix(sn_sv, const char *suffix); static inline bool sn_sv_null(sn_sv it) { - return it.data == NULL && it.length == 0; + return it.data == NULL && it.length == 0; } /* interactive user functions */ @@ -109,7 +114,7 @@ bool sn_yesno(const char *fmt, ...) PRINTF_FORMAT(1, 2); static inline const char * sn_bool_yesno(bool x) { - return x ? "yes" : "no"; + return x ? "yes" : "no"; } char *sn_join_with(const char *items[], size_t items_size, const char *sep);