/* * Copyright (c) 2009-2016 Petri Lehtinen * * Jansson is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #define BUFFER_SIZE (256 * 1024) /* 256 KB */ #define URL_FORMAT "https://api.github.com/repos/%s/%s/commits" #define URL_SIZE 256 /* Return the offset of the first newline in text or the length of text if there's no newline */ static int newline_offset(const char *text) { const char *newline = strchr(text, '\n'); if (!newline) return strlen(text); else return (int)(newline - text); } struct write_result { char *data; int pos; }; static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) { struct write_result *result = (struct write_result *)stream; if (result->pos + size * nmemb >= BUFFER_SIZE - 1) { fprintf(stderr, "error: too small buffer\n"); return 0; } memcpy(result->data + result->pos, ptr, size * nmemb); result->pos += size * nmemb; return size * nmemb; } static char *request(const char *url) { CURL *curl = NULL; CURLcode status; struct curl_slist *headers = NULL; char *data = NULL; long code; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if (!curl) goto error; data = malloc(BUFFER_SIZE); if (!data) goto error; struct write_result write_result = {.data = data, .pos = 0}; curl_easy_setopt(curl, CURLOPT_URL, url); /* GitHub commits API v3 requires a User-Agent header */ headers = curl_slist_append(headers, "User-Agent: Jansson-Tutorial"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); status = curl_easy_perform(curl); if (status != 0) { fprintf(stderr, "error: unable to request data from %s:\n", url); fprintf(stderr, "%s\n", curl_easy_strerror(status)); goto error; } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); if (code != 200) { fprintf(stderr, "error: server responded with code %ld\n", code); goto error; } curl_easy_cleanup(curl); curl_slist_free_all(headers); curl_global_cleanup(); /* zero-terminate the result */ data[write_result.pos] = '\0'; return data; error: if (data) free(data); if (curl) curl_easy_cleanup(curl); if (headers) curl_slist_free_all(headers); curl_global_cleanup(); return NULL; } int main(int argc, char *argv[]) { size_t i; char *text; char url[URL_SIZE]; json_t *root; json_error_t error; if (argc != 3) { fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]); fprintf(stderr, "List commits at USER's REPOSITORY.\n\n"); return 2; } snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]); text = request(url); if (!text) return 1; /*decode the JSON text we got as a response*/ root = json_loads(text, 0, &error); free(text); if (!root) { fprintf(stderr, "error: on line %d: %s\n", error.line, error.text); return 1; } if (!json_is_array(root)) { fprintf(stderr, "error: root is not an array\n"); json_decref(root); return 1; } for (i = 0; i < json_array_size(root); i++) { json_t *data, *sha, *commit, *message; const char *message_text; /*get i'th element of the 'root' array*/ data = json_array_get(root, i); /*Is 'data' a JSON object?*/ if (!json_is_object(data)) { fprintf(stderr, "error: commit data %d is not an object\n", (int)(i + 1)); json_decref(root); return 1; } sha = json_object_get(data, "sha"); if (!json_is_string(sha)) { fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1)); return 1; } commit = json_object_get(data, "commit"); if (!json_is_object(commit)) { fprintf(stderr, "error: commit %d: commit is not an object\n", (int)(i + 1)); json_decref(root); return 1; } message = json_object_get(commit, "message"); if (!json_is_string(message)) { fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1)); json_decref(root); return 1; } /*extract C-style string from a JSON string*/ message_text = json_string_value(message); printf("%.8s %.*s\n", json_string_value(sha), newline_offset(message_text), message_text); } /*decrease the reference count*/ /*this way the recource is released*/ json_decref(root); return 0; }