From c17ed45d618acc66f8eb4f0e19238399ebfad61e Mon Sep 17 00:00:00 2001 From: lobotomia-ops Date: Wed, 17 Jun 2026 14:23:03 +0300 Subject: [PATCH 1/2] feat: add single filter syntax help to -filters flag --- src/filters.c | 18 ++++++++++++++++++ src/filters.h | 2 ++ src/main.c | 19 +++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/filters.c b/src/filters.c index 1b866b3..c77a319 100644 --- a/src/filters.c +++ b/src/filters.c @@ -729,6 +729,24 @@ static const FilterDef FILTERS[] = { }; static const int NFILTERS = (int)(sizeof(FILTERS) / sizeof(FILTERS[0])); + +/* print a filter whose name contains the user input */ +void filters_print_single(const char *filter_name){ + char flag = 0; + + for (int i = 0; i < NFILTERS; i++) { + if (strstr(FILTERS[i].usage, filter_name) != NULL) { + flag ++; + printf(" %s\n", FILTERS[i].usage); + } + } + + if (!flag) { + fprintf(stderr, "error: no filters found matching '%s'\n", filter_name); + } + return; +} + void filters_print_list(void) { printf("Filters (chain with commas, e.g. -vf \"scale=800:-1,grayscale,gblur=2\"):\n\n"); for (int i = 0; i < NFILTERS; i++) diff --git a/src/filters.h b/src/filters.h index 93a2337..364e046 100644 --- a/src/filters.h +++ b/src/filters.h @@ -23,6 +23,8 @@ Image *run_filtergraph(const char *graph, Image *base, AppContext *app, char **e /* Print the filter catalogue to stdout (for `imgcli -filters`). */ void filters_print_list(void); +void filters_print_single(const char *filter_name); + /* Print the filter catalogue as a JSON array of {name, syntax, description} * to stdout (for `imgcli -filters --json`). */ void filters_print_json(void); diff --git a/src/main.c b/src/main.c index 2b9d76a..ccd46c8 100644 --- a/src/main.c +++ b/src/main.c @@ -206,11 +206,18 @@ int main(int argc, char **argv) { int want_filters = 0, want_info = 0, json = 0, quiet = 0, dry_run = 0, fail_fast = 0; char msg[512]; + const char *filter_name = NULL; + for (int i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) { usage(stdout); return 0; } else if (!strcmp(arg, "-V") || !strcmp(arg, "--version")) { printf("imgcli %s\n", IMGCLI_VERSION); return 0; } - else if (!strcmp(arg, "-filters")) want_filters = 1; + else if (!strcmp(arg, "-filters")) { + want_filters = 1; + if (i + 1 < argc && argv[i + 1][0] != '-') { + filter_name = argv[++i]; + } + } else if (!strcmp(arg, "-info")) want_info = 1; else if (!strcmp(arg, "--json")) json = 1; else if (!strcmp(arg, "--quiet")) quiet = 1; @@ -254,7 +261,15 @@ int main(int argc, char **argv) { } } - if (want_filters) { if (json) filters_print_json(); else filters_print_list(); return 0; } + if (want_filters) { + if (filter_name) { + filters_print_single(filter_name); + } else { + if (json) filters_print_json(); + else filters_print_list(); + } + return 0; + } if (ninputs == 0) { emit_error(json, "no input (use -i); try -h for help"); return 2; } From 20325e712efb13785515bd768df57a53cc4d87be Mon Sep 17 00:00:00 2001 From: Stephen Date: Wed, 17 Jun 2026 09:39:34 -0500 Subject: [PATCH 2/2] Refine -filters NAME: match by name, fix exit code, add --json + docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Polishing the contributed per-filter help (#76) before merge: - Match the exact filter NAME, not a substring of the whole usage line. The substring approach matched description text too, so e.g. `-filters V` returned 9 filters and `-filters alias` would have listed every alias. - Return a non-zero exit (1) when no filter matches, instead of printing an error to stderr but exiting 0 — keeps the 0/1/2 contract intact for scripts. - Honour --json for a single filter: emit the same [{name,syntax,description}] shape as `-filters --json`, filtered to the match (reusing split_usage). - Match house style (4-space indent), and document the `-filters [NAME]` form in --help, the README usage block, and the man page. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 3 ++- man/imgcli.1 | 6 ++++-- src/filters.c | 42 ++++++++++++++++++++++++------------------ src/filters.h | 4 +++- src/main.c | 25 ++++++++++--------------- 5 files changed, 43 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 2797847..7e62b27 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,8 @@ imgcli [-i INPUT]... [-vf GRAPH] [-q N] [-f FMT] [-y|-n] [--json] OUTPUT --json emit one machine-readable JSON result line (an array in batch) --quiet suppress the human-readable success line --dry-run validate the filtergraph + report output dims; write nothing - -filters list every filter (add --json for a machine-readable list) + -filters [NAME] list every filter, or just NAME's syntax (add --json for a + machine-readable list) -info print input dimensions and exit -V print version -h help diff --git a/man/imgcli.1 b/man/imgcli.1 index 6f7c828..19ecd90 100644 --- a/man/imgcli.1 +++ b/man/imgcli.1 @@ -94,8 +94,10 @@ Suppress the human-readable success line. .B \-\-dry\-run Validate the filtergraph and report the output dimensions; write nothing. .TP -.B \-filters -List every filter. Add +.BI \-filters " [NAME]" +List every filter, or with +.I NAME +just that one filter's syntax (exit status 1 if no filter has that name). Add .B \-\-json for a machine-readable list. .TP diff --git a/src/filters.c b/src/filters.c index c77a319..af81aad 100644 --- a/src/filters.c +++ b/src/filters.c @@ -729,24 +729,6 @@ static const FilterDef FILTERS[] = { }; static const int NFILTERS = (int)(sizeof(FILTERS) / sizeof(FILTERS[0])); - -/* print a filter whose name contains the user input */ -void filters_print_single(const char *filter_name){ - char flag = 0; - - for (int i = 0; i < NFILTERS; i++) { - if (strstr(FILTERS[i].usage, filter_name) != NULL) { - flag ++; - printf(" %s\n", FILTERS[i].usage); - } - } - - if (!flag) { - fprintf(stderr, "error: no filters found matching '%s'\n", filter_name); - } - return; -} - void filters_print_list(void) { printf("Filters (chain with commas, e.g. -vf \"scale=800:-1,grayscale,gblur=2\"):\n\n"); for (int i = 0; i < NFILTERS; i++) @@ -780,6 +762,30 @@ void filters_print_json(void) { printf("]\n"); } +/* Print one filter's help, looked up by exact name (for `imgcli -filters NAME`). + * With json, emits the same [{name,syntax,description}] array shape as + * filters_print_json, filtered to the single match. Returns 1 if the filter was + * found, 0 otherwise (so the caller can set a non-zero exit code). */ +int filters_print_single(const char *name, int json) { + for (int i = 0; i < NFILTERS; i++) { + if (strcmp(FILTERS[i].name, name) != 0) continue; + if (json) { + char syntax[128]; + const char *desc; + split_usage(FILTERS[i].usage, syntax, sizeof syntax, &desc); + fputs("[\n {\"name\":", stdout); json_str(stdout, FILTERS[i].name); + fputs(",\"syntax\":", stdout); json_str(stdout, syntax); + fputs(",\"description\":", stdout); json_str(stdout, desc); + fputs("}\n]\n", stdout); + } else { + printf(" %s\n", FILTERS[i].usage); + } + return 1; + } + fprintf(stderr, "imgcli: no filter named '%s' (try -filters to list them)\n", name); + return 0; +} + /* trim leading/trailing ascii whitespace in place, returns start */ static char *trim(char *s) { while (*s && isspace((unsigned char)*s)) s++; diff --git a/src/filters.h b/src/filters.h index 364e046..0e83de9 100644 --- a/src/filters.h +++ b/src/filters.h @@ -23,7 +23,9 @@ Image *run_filtergraph(const char *graph, Image *base, AppContext *app, char **e /* Print the filter catalogue to stdout (for `imgcli -filters`). */ void filters_print_list(void); -void filters_print_single(const char *filter_name); +/* Print one filter's help by exact name (`imgcli -filters NAME`), honouring + * --json. Returns 1 if found, 0 if no filter has that name. */ +int filters_print_single(const char *name, int json); /* Print the filter catalogue as a JSON array of {name, syntax, description} * to stdout (for `imgcli -filters --json`). */ diff --git a/src/main.c b/src/main.c index ccd46c8..b7fc18f 100644 --- a/src/main.c +++ b/src/main.c @@ -55,7 +55,7 @@ static void usage(FILE *f) { " --json emit a single machine-readable JSON result line (array in batch)\n" " --quiet suppress the human-readable success line\n" " --dry-run validate the filtergraph and report output dims; write nothing\n" - " -filters list available filters and exit\n" + " -filters [NAME] list filters (or, with NAME, one filter's syntax) and exit\n" " -info print info about each input and exit\n" " -V, --version print version and exit\n" " -h, --help show this help\n" @@ -205,18 +205,16 @@ int main(int argc, char **argv) { int overwrite = -1; /* -1 ask/refuse, 1 = -y, 0 = -n */ int want_filters = 0, want_info = 0, json = 0, quiet = 0, dry_run = 0, fail_fast = 0; char msg[512]; - - const char *filter_name = NULL; + const char *filter_name = NULL; /* `-filters NAME` -> help for one filter */ for (int i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) { usage(stdout); return 0; } else if (!strcmp(arg, "-V") || !strcmp(arg, "--version")) { printf("imgcli %s\n", IMGCLI_VERSION); return 0; } else if (!strcmp(arg, "-filters")) { - want_filters = 1; - if (i + 1 < argc && argv[i + 1][0] != '-') { - filter_name = argv[++i]; - } + want_filters = 1; + /* an optional non-flag token names a single filter to describe */ + if (i + 1 < argc && argv[i + 1][0] != '-') filter_name = argv[++i]; } else if (!strcmp(arg, "-info")) want_info = 1; else if (!strcmp(arg, "--json")) json = 1; @@ -261,14 +259,11 @@ int main(int argc, char **argv) { } } - if (want_filters) { - if (filter_name) { - filters_print_single(filter_name); - } else { - if (json) filters_print_json(); - else filters_print_list(); - } - return 0; + if (want_filters) { + if (filter_name) return filters_print_single(filter_name, json) ? 0 : 1; + if (json) filters_print_json(); + else filters_print_list(); + return 0; } if (ninputs == 0) { emit_error(json, "no input (use -i); try -h for help"); return 2; }