xref: /NextBSD/contrib/jansson/doc/github_commits.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
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