github_commits.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * Copyright (c) 2009-2013 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. char *data = NULL;
  46. long code;
  47. curl_global_init(CURL_GLOBAL_ALL);
  48. curl = curl_easy_init();
  49. if(!curl)
  50. goto error;
  51. data = malloc(BUFFER_SIZE);
  52. if(!data)
  53. goto error;
  54. struct write_result write_result = {
  55. .data = data,
  56. .pos = 0
  57. };
  58. curl_easy_setopt(curl, CURLOPT_URL, url);
  59. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
  60. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result);
  61. status = curl_easy_perform(curl);
  62. if(status != 0)
  63. {
  64. fprintf(stderr, "error: unable to request data from %s:\n", url);
  65. fprintf(stderr, "%s\n", curl_easy_strerror(status));
  66. goto error;
  67. }
  68. curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
  69. if(code != 200)
  70. {
  71. fprintf(stderr, "error: server responded with code %ld\n", code);
  72. goto error;
  73. }
  74. curl_easy_cleanup(curl);
  75. curl_global_cleanup();
  76. /* zero-terminate the result */
  77. data[write_result.pos] = '\0';
  78. return data;
  79. error:
  80. if(data)
  81. free(data);
  82. if(curl)
  83. curl_easy_cleanup(curl);
  84. curl_global_cleanup();
  85. return NULL;
  86. }
  87. int main(int argc, char *argv[])
  88. {
  89. size_t i;
  90. char *text;
  91. char url[URL_SIZE];
  92. json_t *root;
  93. json_error_t error;
  94. if(argc != 3)
  95. {
  96. fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
  97. fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
  98. return 2;
  99. }
  100. snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
  101. text = request(url);
  102. if(!text)
  103. return 1;
  104. root = json_loads(text, 0, &error);
  105. free(text);
  106. if(!root)
  107. {
  108. fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
  109. return 1;
  110. }
  111. if(!json_is_array(root))
  112. {
  113. fprintf(stderr, "error: root is not an array\n");
  114. json_decref(root);
  115. return 1;
  116. }
  117. for(i = 0; i < json_array_size(root); i++)
  118. {
  119. json_t *data, *sha, *commit, *message;
  120. const char *message_text;
  121. data = json_array_get(root, i);
  122. if(!json_is_object(data))
  123. {
  124. fprintf(stderr, "error: commit data %d is not an object\n", (int)(i + 1));
  125. json_decref(root);
  126. return 1;
  127. }
  128. sha = json_object_get(data, "sha");
  129. if(!json_is_string(sha))
  130. {
  131. fprintf(stderr, "error: commit %d: sha is not a string\n", (int)(i + 1));
  132. return 1;
  133. }
  134. commit = json_object_get(data, "commit");
  135. if(!json_is_object(commit))
  136. {
  137. fprintf(stderr, "error: commit %d: commit is not an object\n", (int)(i + 1));
  138. json_decref(root);
  139. return 1;
  140. }
  141. message = json_object_get(commit, "message");
  142. if(!json_is_string(message))
  143. {
  144. fprintf(stderr, "error: commit %d: message is not a string\n", (int)(i + 1));
  145. json_decref(root);
  146. return 1;
  147. }
  148. message_text = json_string_value(message);
  149. printf("%.8s %.*s\n",
  150. json_string_value(sha),
  151. newline_offset(message_text),
  152. message_text);
  153. }
  154. json_decref(root);
  155. return 0;
  156. }