1/* 2 * Copyright (c) 2009-2014 Petri Lehtinen <petri@digip.org> 3 * 4 * Jansson is free software; you can redistribute it and/or modify 5 * it under the terms of the MIT license. See LICENSE for details. 6 */ 7 8#include <stdlib.h> 9#include <string.h> 10 11#include <jansson.h> 12#include <curl/curl.h> 13 14#define BUFFER_SIZE (256 * 1024) /* 256 KB */ 15 16#define URL_FORMAT "https://api.github.com/repos/%s/%s/commits" 17#define URL_SIZE 256 18 19/* Return the offset of the first newline in text or the length of 20 text if there's no newline */ 21static int newline_offset(const char *text) 22{ 23 const char *newline = strchr(text, '\n'); 24 if(!newline) 25 return strlen(text); 26 else 27 return (int)(newline - text); 28} 29 30struct write_result 31{ 32 char *data; 33 int pos; 34}; 35 36static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) 37{ 38 struct write_result *result = (struct write_result *)stream; 39 40 if(result->pos + size * nmemb >= BUFFER_SIZE - 1) 41 { 42 fprintf(stderr, "error: too small buffer\n"); 43 return 0; 44 } 45 46 memcpy(result->data + result->pos, ptr, size * nmemb); 47 result->pos += size * nmemb; 48 49 return size * nmemb; 50} 51 52static char *request(const char *url) 53{ 54 CURL *curl = NULL; 55 CURLcode status; 56 struct curl_slist *headers = NULL; 57 char *data = NULL; 58 long code; 59 60 curl_global_init(CURL_GLOBAL_ALL); 61 curl = curl_easy_init(); 62 if(!curl) 63 goto error; 64 65 data = malloc(BUFFER_SIZE); 66 if(!data) 67 goto error; 68 69 struct write_result write_result = { 70 .data = data, 71 .pos = 0 72 }; 73 74 curl_easy_setopt(curl, CURLOPT_URL, url); 75 76 /* GitHub commits API v3 requires a User-Agent header */ 77 headers = curl_slist_append(headers, "User-Agent: Jansson-Tutorial"); 78 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 79 80 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response); 81 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); 82 83 status = curl_easy_perform(curl); 84 if(status != 0) 85 { 86 fprintf(stderr, "error: unable to request data from %s:\n", url); 87 fprintf(stderr, "%s\n", curl_easy_strerror(status)); 88 goto error; 89 } 90 91 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); 92 if(code != 200) 93 { 94 fprintf(stderr, "error: server responded with code %ld\n", code); 95 goto error; 96 } 97 98 curl_easy_cleanup(curl); 99 curl_slist_free_all(headers); 100 curl_global_cleanup(); 101 102 /* zero-terminate the result */ 103 data[write_result.pos] = '\0'; 104 105 return data; 106 107error: 108 if(data) 109 free(data); 110 if(curl) 111 curl_easy_cleanup(curl); 112 if(headers) 113 curl_slist_free_all(headers); 114 curl_global_cleanup(); 115 return NULL; 116} 117 118int main(int argc, char *argv[]) 119{ 120 size_t i; 121 char *text; 122 char url[URL_SIZE]; 123 124 json_t *root; 125 json_error_t error; 126 127 if(argc != 3) 128 { 129 fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]); 130 fprintf(stderr, "List commits at USER's REPOSITORY.\n\n"); 131 return 2; 132 } 133 134 snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]); 135 136 text = request(url); 137 if(!text) 138 return 1; 139 140 root = json_loads(text, 0, &error); 141 free(text); 142 143 if(!root) 144 { 145 fprintf(stderr, "error: on line %d: %s\n", error.line, error.text); 146 return 1; 147 } 148 149 if(!json_is_array(root)) 150 { 151 fprintf(stderr, "error: root is not an array\n"); 152 json_decref(root); 153 return 1; 154 } 155 156 for(i = 0; i < json_array_size(root); i++) 157 { 158 json_t *data, *sha, *commit, *message; 159 const char *message_text; 160 161 data = json_array_get(root, i); 162 if(!json_is_object(data)) 163 { 164 fprintf(stderr, "error: commit data %d is not an object\n", (int)(i + 1)); 165 json_decref(root); 166 return 1; 167 } 168 169 sha = json_object_get(data, "sha"); 170 if(!json_is_string(sha)) 171 { 172 fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1)); 173 return 1; 174 } 175 176 commit = json_object_get(data, "commit"); 177 if(!json_is_object(commit)) 178 { 179 fprintf(stderr, "error: commit %d: commit is not an object\n", (int)(i + 1)); 180 json_decref(root); 181 return 1; 182 } 183 184 message = json_object_get(commit, "message"); 185 if(!json_is_string(message)) 186 { 187 fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1)); 188 json_decref(root); 189 return 1; 190 } 191 192 message_text = json_string_value(message); 193 printf("%.8s %.*s\n", 194 json_string_value(sha), 195 newline_offset(message_text), 196 message_text); 197 } 198 199 json_decref(root); 200 return 0; 201} 202