💾 Archived View for gemini.rmf-dev.com › repo › Vaati › tuimarket › files › 3d413e1b46c15fa5937d3873… captured on 2023-03-20 at 18:37:11. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

0 #include <stdio.h>

1 #include <stdlib.h>

2 #include <stdint.h>

3 #include <unistd.h>

4 #include <fcntl.h>

5 #include <string.h>

6 #include <pwd.h>

7 #include <time.h>

8 #include <pthread.h>

9 #include <errno.h>

10 #include <curl/curl.h>

11 #include "termbox.h"

12 #include "strlcpy.h"

13

14 #ifndef PATH_MAX

15 #define PATH_MAX 1024

16 #endif

17 #define SIZEOF(X) sizeof(X) / sizeof(*X)

18

19 #define INTERVAL 5 /* update informations every x seconds */

20

21 struct symbol {

22 char symbol[16];

23 char name[256];

24 char state[128];

25 float price;

26 float previous_price;

27 struct symbol *next;

28 };

29 struct symbol *symbols = NULL;

30

31 const char query[] =

32 "https://query1.finance.yahoo.com/v7/finance/"

33 "quote?lang=en-US&region=US&corsDomain=finance.yahoo.com&"

34 "fields=regularMarketChange,regularMarketPrice,shortName&"

35 "symbols=%s";

36

37 char *url = NULL;

38

39 const char *paths[] = {

40 ".config/tuimarket/symbols",

41 ".tuimarket/symbols",

42 ".tuimarket_symbols",

43 };

44

45 struct mem {

46 char *memory;

47 size_t size;

48 };

49 struct mem chunk;

50

51 static size_t writecb(void *contents, size_t size, size_t nmemb, void *userp) {

52

53 size_t realsize = size * nmemb;

54 struct mem *mem = (struct mem*)userp;

55

56 mem->memory = realloc(mem->memory, mem->size + realsize + 1);

57 if(mem->memory == NULL) {

58 printf("not enough memory\n");

59 return 0;

60 }

61

62 memcpy(&(mem->memory[mem->size]), contents, realsize);

63 mem->size += realsize;

64 mem->memory[mem->size] = 0;

65 return realsize;

66 }

67

68 char *handle_url(char *url, size_t *len) {

69

70 CURL *curl_handle;

71 CURLcode res;

72

73 chunk.memory = malloc(1);

74 chunk.size = 0;

75

76 curl_handle = curl_easy_init();

77 curl_easy_setopt(curl_handle, CURLOPT_URL, url);

78 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writecb);

79 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&chunk);

80 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");

81

82 res = curl_easy_perform(curl_handle);

83 curl_easy_cleanup(curl_handle);

84

85 if(res != CURLE_OK) {

86 printf("curl_easy_perform() failed: %s\n",

87 curl_easy_strerror(res));

88 return NULL;

89 }

90

91 *len = chunk.size;

92

93 return chunk.memory;

94 }

95

96 static ssize_t get_home(char *buf, size_t length) {

97

98 struct passwd *pw;

99 char *home;

100 int fd;

101

102 home = getenv("HOME");

103 if (home) {

104 fd = open(home, O_DIRECTORY);

105 if (fd > -1) {

106 close(fd);

107 return strlcpy(buf, home, length);

108 }

109 }

110

111 pw = getpwuid(geteuid());

112 if (!pw) return -1;

113 fd = open(pw->pw_dir, O_DIRECTORY);

114 if (fd < 0) {

115 close(fd);

116 return -1;

117 }

118 return strlcpy(buf, pw->pw_dir, length);

119 }

120

121 static int load_symbols() {

122

123 FILE *f = NULL;

124 size_t i;

125 ssize_t len;

126 char home[PATH_MAX], path[PATH_MAX];

127

128 len = get_home(home, sizeof(home));

129 if (len == -1) return -1;

130

131 i = 0;

132 while (i < SIZEOF(paths)) {

133 snprintf(path, sizeof(path), "%s/%s", home, paths[i++]);

134 f = fopen(path, "r");

135 if (f) break;

136 }

137 if (!f) return -1;

138

139 while (1) {

140

141 struct symbol s = {0}, *new;

142 size_t len;

143

144 if (!fgets(s.symbol, sizeof(s.symbol), f)) break;

145

146 len = strnlen(s.symbol, sizeof(s.symbol));

147 if (!len || len > sizeof(s.symbol)) break;

148

149 if (s.symbol[len - 1] == '\n') s.symbol[len - 1] = '\0';

150

151 new = malloc(sizeof(struct symbol));

152 if (!new) return -1;

153

154 *new = s;

155 new->next = symbols;

156 symbols = new;

157 }

158 fclose(f);

159

160 return 0;

161 }

162

163 const char str_price[] = "\"regularMarketPrice\":";

164 const char str_old_price[] = "\"regularMarketPreviousClose\":";

165 const char str_state[] = "\"marketState\":\"";

166 const char str_name[] = "\"shortName\":\"";

167

168 static int find_copy(const char *haystack, const char *needle,

169 size_t needle_len, char stop, char *buf, size_t len) {

170

171 char *start, *end;

172

173 start = strstr(haystack, needle);

174 if (!start) return -1;

175

176 start += needle_len - 1;

177 end = start;

178 while (*end && *end != stop) end++;

179

180 if (!*end || (size_t)(end - start) > len) return -1;

181

182 memcpy(buf, start, end - start);

183 buf[end - start] = '\0';

184

185 return 0;

186 }

187

188 static char* make_url() {

189

190 size_t len, i;

191 struct symbol *symbol;

192 char *list, *data;

193

194 len = 0;

195 symbol = symbols;

196 while (symbol) {

197 len += strnlen(symbol->symbol, sizeof(symbol->symbol)) + 1;

198 symbol = symbol->next;

199 }

200 len++;

201

202 list = malloc(len);

203 if (!list) {

204 printf("not enough memory\n");

205 return NULL;

206 }

207

208 i = 0;

209 symbol = symbols;

210 while (symbol) {

211 i += strlcpy(&list[i], symbol->symbol, len - i);

212 list[i++] = ',';

213 symbol = symbol->next;

214 }

215 list[i - 1] = '\0';

216

217 len += sizeof(query);

218 data = malloc(len);

219 snprintf(data, len, query, list);

220

221 free(list);

222

223 return data;

224 }

225

226 static int update_symbols() {

227

228 struct symbol *symbol;

229 char buf[64], *start, *end, *data;

230 size_t len;

231 int ret = -1;

232

233 data = handle_url(url, &len);

234 if (!data) return -1;

235

236 symbol = symbols;

237 start = data;

238 end = start + len - 1;

239 *end = 0;

240 next:

241 if (find_copy(data, str_price, sizeof(str_price), ',', buf,

242 sizeof(buf)))

243 goto clean;

244 symbol->price = atof(buf);

245

246 if (find_copy(data, str_old_price, sizeof(str_old_price), ',', buf,

247 sizeof(buf)))

248 goto clean;

249 symbol->previous_price = atof(buf);

250

251 if (find_copy(data, str_state, sizeof(str_state), '"', symbol->state,

252 sizeof(symbol->state)))

253 goto clean;

254

255 if (find_copy(data, str_name, sizeof(str_name), '"', symbol->name,

256 sizeof(symbol->name)))

257 goto clean;

258

259 data = strstr(data, str_price);

260 while (data && *data && data < end) {

261 if (*data == '{') {

262 if (!strstr(data, str_price)) break;

263 symbol = symbol->next;

264 goto next;

265 }

266 data++;

267 }

268

269 ret = 0;

270 clean:

271 free(start);

272

273 return ret;

274 }

275

276 void *update_thread(void *ptr) {

277 int *run = ptr, counter;

278 while (*run) {

279 update_symbols();

280 counter = 0;

281 while (counter++ < INTERVAL && *run)

282 sleep(1);

283 }

284 return ptr;

285 }

286

287 #define COL_SYMBOL 2

288 #define COL_NAME (COL_SYMBOL + sizeof("Symbol |") + 1)

289 #define COL_VARIATION (-(signed)sizeof("Variation") - 8)

290 #define COL_PRICE (COL_VARIATION -(signed)sizeof("| Price") - 3)

291

292 int main(int argc, char *argv[]) {

293

294 int scroll = 0, run;

295 pthread_t thread;

296

297 if (!argc) return sizeof(*argv);

298

299 if (load_symbols()) {

300 printf("cannot find symbols file\n");

301 return -1;

302 }

303

304 url = make_url();

305 if (!url) return -1;

306

307 curl_global_init(CURL_GLOBAL_ALL);

308

309 if (tb_init()) {

310 printf("tb_init: %s\n", strerror(errno));

311 return -1;

312 }

313

314 run = 1;

315 pthread_create(&thread, NULL, update_thread, &run);

316

317 while (1) {

318

319 struct tb_event ev;

320 struct symbol *symbol;

321 int i, w, h, bottom;

322

323 w = tb_width();

324 h = tb_height();

325

326 tb_clear();

327 i = 0;

328 while (i++ < w) tb_set_cell(i, 0, ' ', TB_BLACK, TB_WHITE);

329

330 tb_print(COL_SYMBOL - 2, 0, TB_BLACK, TB_WHITE, " Symbol");

331 tb_print(COL_NAME - 2, 0, TB_BLACK, TB_WHITE, "| Name");

332 tb_print(w + COL_PRICE - 2, 0, TB_BLACK, TB_WHITE, "| Price");

333 tb_print(w + COL_VARIATION - 2, 0,

334 TB_BLACK, TB_WHITE, "| Variation");

335

336 i = 1;

337 symbol = symbols;

338 bottom = 1;

339 while (symbol) {

340 int gain, j;

341 if (scroll >= i) {

342 symbol = symbol->next;

343 i++;

344 continue;

345 }

346 gain = (symbol->price >= symbol->previous_price);

347 tb_print(COL_SYMBOL, i - scroll,

348 TB_DEFAULT, TB_DEFAULT,

349 symbol->symbol);

350 tb_print(COL_NAME, i - scroll, TB_DEFAULT, TB_DEFAULT,

351 symbol->name);

352

353 j = w + COL_PRICE - 2;

354 while (j++ < w)

355 tb_set_cell(j, i, ' ', TB_DEFAULT, TB_DEFAULT);

356 tb_printf(w + COL_PRICE, i - scroll,

357 TB_DEFAULT, TB_DEFAULT,

358 "%.2f", symbol->price);

359 tb_printf(w + COL_VARIATION + gain, i - scroll,

360 gain ? TB_GREEN : TB_RED,

361 TB_DEFAULT, "%.2f (%.2f%%)",

362 symbol->price - symbol->previous_price,

363 symbol->price / symbol->previous_price

364 * 100 - 100);

365 symbol = symbol->next;

366 if (i - scroll > h) {

367 bottom = 0;

368 break;

369 }

370 i++;

371 }

372

373 tb_present();

374

375 if (!tb_peek_event(&ev, 1000)) {

376 if (ev.key == TB_KEY_ESC || ev.ch == 'q') break;

377 if ((ev.key == TB_KEY_ARROW_DOWN || ev.ch == 'j') &&

378 !bottom) scroll++;

379 if ((ev.key == TB_KEY_ARROW_UP || ev.ch == 'k') &&

380 scroll) scroll--;

381 }

382

383 }

384

385 run = 0;

386 tb_shutdown();

387 curl_global_cleanup();

388 free(url);

389 while (symbols) {

390 struct symbol *s = symbols;

391 symbols = symbols->next;

392 free(s);

393 }

394

395 return 0;

396 }

397