github_commits.c 4.7 KB

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