From b0e34a58ea88a7329c02f980721dec0ba7590dbf Mon Sep 17 00:00:00 2001 From: Micheal Smith Date: Mon, 15 Dec 2025 02:15:12 -0600 Subject: [PATCH] Added some required field checking. --- meson.build | 4 +- omdb.c | 119 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/meson.build b/meson.build index cfe591a..eb4cbd9 100644 --- a/meson.build +++ b/meson.build @@ -9,7 +9,9 @@ project( curl = dependency('libcurl') cpp_flags = [] -c_flags = ['-DPOSIXLY_CORRECT', '-Wno-c99-extensions'] +c_flags = ['-DPOSIXLY_CORRECT', + '-D_DEFAULT_SOURCE', + '-Wno-c99-extensions'] dependencies = [ curl diff --git a/omdb.c b/omdb.c index 9beb0a8..41f2b67 100644 --- a/omdb.c +++ b/omdb.c @@ -16,6 +16,13 @@ #define OMDB_URL "http://www.omdbapi.com" #define PROJECT_NAME "omdb" +const char *TITLE_LOOKUP_KEYS[] = { + "Actors", "Awards", "BoxOffice", "Country", "DVD", "Director", + "Genre", "Language", "Metascore", "Plot", "Production", + "Rated", "Ratings", "Released", "Response", "Runtime", "Title", + "Type", "Website", "Writer", "Year", "imdbID", "imdbRating", + "imdbVotes", NULL}; + /* Default to plain output. */ static enum _output_format { OUTPUT_JSON, @@ -23,27 +30,49 @@ static enum _output_format { OUTPUT_FANCY } output_format = OUTPUT_PLAIN; -static struct option opts[] = {{"id", no_argument, NULL, 'i'}, +static struct option opts[] = {{"api-key", required_argument, NULL, 'k'}, + {"id", no_argument, NULL, 'i'}, {"json", no_argument, NULL, 'j'}, - {"api-key", required_argument, NULL, 'k'}, + {"full-plot", no_argument, NULL, 'f'}, {"search-type", required_argument, NULL, 't'}, + {"year", required_argument, NULL, 'y'}, {NULL, 0, NULL, 0}}; void print_usage(void) { printf( PROJECT_NAME - " [ikt] \n" + " \n" "\t-i/--id: Search by ID rather than title. (optional)\n" "\t-j/--json: Output full JSON response.\n" "\t-k/--api-key: OMDB API Key. (required)\n" - "\t-t/--search-type: Search type. (optional) [movie, series, episode]\n"); + "\t-f/--full-plot: Output long plot description.\n" + "\t-t/--search-type: Search type. (optional) [movie, series, episode]\n" + "\t-y/--year: Year of release.\n"); +} + +CURLU *init_curl_url(const char *restrict ustr) { + /* Just validate and add options directly to the request now. */ + CURLU *url = curl_url(); + CURLUcode ures; + + if (url == NULL) { + fputs("Out of memory.\n", stderr); + exit(EXIT_FAILURE); + } + + if ((ures = curl_url_set(url, CURLUPART_URL, ustr, 0)) != CURLUE_OK) { + fprintf(stderr, "Couldn't set url: %s\n", curl_url_strerror(ures)); + return NULL; + } + + return url; } /* Set a URL query parameter. */ void set_param(CURLU *restrict url, const char *restrict key, char *restrict value) { CURLUcode err; - char param[2048]; + char param[256]; snprintf(param, sizeof param, "%s=%s", key, value); @@ -79,22 +108,61 @@ int print_title_json(const cJSON *const json) { return 0; } +/* Return 0 if all required fields for a title / id check are present. */ +int verify_title_fields(const cJSON *restrict json) { + const char **key = TITLE_LOOKUP_KEYS; + + while (*key != NULL) { + + if (cJSON_GetObjectItem(json, *key) == NULL) { + fprintf(stderr, "Missing field '%s' in JSON.\n", *key); + return -1; + } + + key++; + } + + return 0; +} + /* Print some title information in plain text. */ -int print_title_plain(const cJSON *json) { - cJSON *title = cJSON_GetObjectItem(json, "title"); - cJSON *imdb_rating = cJSON_GetObjectItem(json, "imdbRating"); - - if (title == NULL || imdb_rating == NULL) { - printf("Could not find title or rating in response.\n"); +int print_title_plain(const cJSON *restrict json) { + if (verify_title_fields(json) != 0) return -1; - } - printf("Found: '%s' with rating of %s\n", title->valuestring, - imdb_rating->valuestring); + cJSON *title = cJSON_GetObjectItem(json, "Title"); + cJSON *rated = cJSON_GetObjectItem(json, "Rated"); + cJSON *released = cJSON_GetObjectItem(json, "Released"); + + printf("%s [%s]\n", title->valuestring, rated->valuestring); + printf("Released on %s\n", released->valuestring); return 0; } +int print_title_fancy(const cJSON *restrict json) { + /* Bad looking, but attempts to fetch, and show the poster if possible. */ + cJSON *poster = cJSON_GetObjectItem(json, "Poster"); + if (poster != NULL) { + + struct fetch *f = fetch_init(); + CURLU *url = init_curl_url(poster->valuestring); + + if (url != NULL) { + + struct response *resp = fetch(f, url); + + if (resp != NULL) { + process_image_mem((const unsigned char *)resp->data, resp->size); + response_cleanup(resp); + fetch_cleanup(f); + } + } + } + + return print_title_plain(json); +} + int handle_response(struct response *restrict resp) { cJSON *json = NULL; @@ -143,20 +211,13 @@ int main(int argc, char **argv) { } /* Just validate and add options directly to the request now. */ - CURLU *url = curl_url(); - CURLUcode ures; - + CURLU *url = init_curl_url(OMDB_URL); if (url == NULL) { - fputs("Out of memory.\n", stderr); + fputs("Can't set url for retrieval.\n", stderr); exit(EXIT_FAILURE); - } + } - if ((ures = curl_url_set(url, CURLUPART_URL, OMDB_URL, 0)) != CURLUE_OK) { - fprintf(stderr, "Couldn't set url: %s\n", curl_url_strerror(ures)); - exit(EXIT_FAILURE); - } - - while ((ch = getopt_long(argc, argv, "hijk:t:", opts, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "hijk:ft:y:", opts, NULL)) != -1) { switch (ch) { case 'i': /* Lookup by ID */ @@ -170,10 +231,16 @@ int main(int argc, char **argv) { k_flag = true; set_param(url, "apikey", optarg); break; + case 'f': + set_param(url, "plot", "full"); + break; case 't': /* Type: movie, series, episode */ strlcpy(search_mode, optarg, sizeof search_mode); break; + case 'y': + set_param(url, "year", optarg); + break; case '?': fprintf(stderr, "Invalid option'%c'.\n", optopt); exit(EXIT_FAILURE); @@ -209,7 +276,7 @@ int main(int argc, char **argv) { struct fetch *f = fetch_init(); /* Check this */ struct response *resp = fetch(f, url); handle_response(resp); - process_image("./test.jpg"); + /* process_image("./test.jpg"); */ free(resp->data); free(resp); curl_url_cleanup(url);