diff --git a/github-commits/main.c b/github-commits/main.c new file mode 100644 index 0000000..c020f46 --- /dev/null +++ b/github-commits/main.c @@ -0,0 +1,180 @@ +/* + * 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; + + 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; + + data = json_array_get(root, i); + 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; + } + + message_text = json_string_value(message); + printf("%.8s %.*s\n", json_string_value(sha), newline_offset(message_text), + message_text); + } + + json_decref(root); + return 0; +} diff --git a/github-commits/makefile b/github-commits/makefile new file mode 100644 index 0000000..12d6c3c --- /dev/null +++ b/github-commits/makefile @@ -0,0 +1,24 @@ +# Docu +# +#$@ is the name of the file to be made +#$? is the names of the changed dependents +#$< the name of the related file that caused the action +#$* the prefix shared by target and dependent files +# +# Others +RM = /bin/rm -f +# +# Source, Executable, Includes, Library Defines +EXE = main +# +# Compiler, Linker Defines +CC = /usr/bin/gcc +# +all: main.o + $(CC) main.c -L/usr/lib/x86_64-linux-gnu -lcurl -ljansson -Wall -o $(EXE) +# +main.o: + $(CC) -c main.c +# Clean Up Objects, Exectuables, Dumps out of source directory +clean: + $(RM) *.o $(EXE) *~ diff --git a/github-commits/readme.md b/github-commits/readme.md new file mode 100644 index 0000000..52efa42 --- /dev/null +++ b/github-commits/readme.md @@ -0,0 +1,17 @@ +* build + +``` +make clean all +``` + +* cleanup + +``` +make clean +``` + +* run + +``` +./main +```