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 */
newline_offset(const char * text)21 static 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
30 struct write_result
31 {
32 char *data;
33 int pos;
34 };
35
write_response(void * ptr,size_t size,size_t nmemb,void * stream)36 static 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
request(const char * url)52 static 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
107 error:
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
main(int argc,char * argv[])118 int 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