cpc.c (4925B)
1 #include <stdio.h> 2 #include <pwd.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <cjson/cJSON.h> 7 #include <curl/curl.h> 8 9 #include "config.h" 10 11 #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) 12 13 typedef enum {AS_FIAT, AS_CRYPTO} Action; 14 15 typedef struct 16 { 17 char *response; 18 size_t size; 19 } Memory; 20 21 static size_t writefunc(char *data, size_t size, size_t nmemb, Memory *mem); 22 static void wfile(cJSON *array); 23 static void parse_json_object(const char *json_string); 24 static char * replace_home(char *buf, const char *path); 25 static void do_action(Action action, const double value); 26 static void call_api(); 27 static char * cpstr(char *dest, const char *src); 28 static void fatal(const char *fmt, ...); 29 30 static char file[PATH_MAX]; 31 32 int 33 main(int argc, char **argv) 34 { 35 int opt, update = 0; 36 double value = 1; 37 Action action = AS_CRYPTO; 38 39 replace_home(file, PRICES_FILE); 40 41 while ((opt = getopt(argc, argv, "urv:")) != -1) { 42 switch (opt) { 43 case 'u': 44 update = 1; 45 break; 46 case 'r': 47 action = AS_FIAT; 48 break; 49 case 'v': 50 sscanf(optarg, "%lf", &value); 51 break; 52 default: 53 fatal("Unknown option: %c\n", optopt); 54 break; 55 } 56 } 57 58 for(; optind < argc; optind++) 59 printf("ignored arguments: %s\n", argv[optind]); 60 61 if (update) 62 call_api(); 63 64 do_action(action, value); 65 } 66 67 size_t 68 writefunc(char *data, size_t size, size_t nmemb, Memory *mem) 69 { 70 char *ptr; 71 size_t realsize = size * nmemb; 72 73 if ((ptr = realloc(mem->response, mem->size + realsize + 1)) == NULL) 74 fatal("Realloc failed\n"); 75 76 mem->response = ptr; 77 memcpy(&(mem->response[mem->size]), data, realsize); 78 mem->size += realsize; 79 mem->response[mem->size] = 0; 80 81 return realsize; 82 } 83 84 void 85 wfile(cJSON *array) 86 { 87 cJSON *item = array ? array->child : 0; 88 FILE *fp; 89 90 if ((fp = fopen(file, "w+")) == NULL) 91 fatal("Opening file failed: %s\n", file); 92 93 while (item) { 94 cJSON *name = cJSON_GetObjectItem(item, "name"); 95 cJSON *price = cJSON_GetObjectItem(item, "current_price"); 96 fprintf(fp, "%s;%f\n", name->valuestring, price->valuedouble); 97 item=item->next; 98 } 99 100 fclose(fp); 101 } 102 103 void 104 parse_json_object(const char *json_string) 105 { 106 cJSON *json = cJSON_Parse(json_string); 107 108 if (json == NULL) { 109 const char *error_ptr = cJSON_GetErrorPtr(); 110 if (error_ptr != NULL) 111 fatal("Error before: %s\n", error_ptr); 112 return; 113 } 114 wfile(json); 115 cJSON_Delete(json); 116 } 117 118 char * 119 replace_home(char *buf, const char *path) 120 { 121 char *userhome, *envv = "$HOME"; 122 struct passwd userinf; 123 124 userinf = *getpwuid(getuid()); 125 userhome = strdup(userinf.pw_dir); 126 127 if ((strstr(path, envv) != NULL)) { 128 cpstr(buf, userhome); 129 strcat(buf, path + strlen(envv)); 130 } else 131 cpstr(buf, path); 132 133 free(userhome); 134 135 return buf; 136 } 137 138 void 139 do_action(Action action, const double value) 140 { 141 char name[50], *line_buf = NULL; 142 size_t line_buf_size = 0; 143 ssize_t line_size; 144 double price; 145 FILE *fp; 146 ; 147 if ((fp = fopen(file, "r")) == NULL) 148 fatal("Error opening file '%s'\n", file); 149 150 while ((getline(&line_buf, &line_buf_size, fp)) >= 0) { 151 sscanf(line_buf, "%[^;];%lf", name, &price); 152 switch (action) { 153 case AS_FIAT: 154 printf("%f %s\n", value/price, name); 155 break; 156 case AS_CRYPTO: 157 printf("(%s) %f %s\n", name, value*price, FIAT); 158 break; 159 default: 160 break; 161 } 162 } 163 164 free(line_buf); 165 166 fclose(fp); 167 } 168 169 void 170 call_api() 171 { 172 char libversion[20], request[strlen(URL) + strlen(FIAT) + strlen(CURRENCIES) + 1]; 173 struct curl_slist *headers = NULL; 174 Memory mem = {0}; 175 176 CURL *curl = curl_easy_init(); 177 178 if (curl) { 179 headers = curl_slist_append(headers, "Content-Type: application/json"); 180 headers = curl_slist_append(headers, "charset: utf-8"); 181 182 curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW); 183 sprintf(libversion, "curl/%u.%u.%u", 184 (ver->version_num >> 16) & 0xff, 185 (ver->version_num >> 8) & 0xff, 186 ver->version_num & 0xff); 187 sprintf(request, URL, CURRENCIES, FIAT); 188 189 CURLcode res; 190 curl_easy_setopt(curl, CURLOPT_URL, request); 191 curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); 192 curl_easy_setopt(curl, CURLOPT_USERAGENT, libversion); 193 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 194 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); 195 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem); 196 curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); 197 198 res = curl_easy_perform(curl); 199 200 if (res != CURLE_OK) 201 fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); 202 else { 203 parse_json_object(mem.response); 204 printf("\n=== Updated prices ===\n"); 205 free(mem.response); 206 } 207 curl_slist_free_all(headers); 208 } 209 210 curl_easy_cleanup(curl); 211 } 212 213 char * 214 cpstr(char *dest, const char *src) 215 { 216 size_t cplen = strlen(src); 217 memcpy(dest, src, cplen); 218 dest[cplen] = '\0'; 219 return dest; 220 } 221 222 void 223 fatal(const char *fmt, ...) 224 { 225 va_list ap; 226 227 va_start(ap, fmt); 228 vfprintf(stderr, fmt, ap); 229 va_end(ap); 230 231 exit(EXIT_FAILURE); 232 }