💾 Archived View for gemini.rmf-dev.com › repo › Vaati › tuimarket › files › a10d98bca8d4527aa4f8fbf3… captured on 2023-09-08 at 16:45:24. Gemini links have been rewritten to link to archived content

View Raw

More Information

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

Go Back

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 #include "strnstr.h"

14 #include "config.h"

15

16 #ifndef PATH_MAX

17 #define PATH_MAX 1024

18 #endif

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

20

21 const char alloc_fail[] = "memory allocation failure\n";

22

23 struct symbol {

24 char symbol[16];

25 char name[256];

26 float price;

27 float previous_price;

28 };

29 struct symbol *symbols = NULL;

30 size_t symbols_length = 0;

31

32 const char query_price[] =

33 "https://query2.finance.yahoo.com/v7/finance/options/%s";

34

35 const char *paths[] = {

36 ".config/tuimarket/symbols",

37 ".tuimarket/symbols",

38 ".tuimarket_symbols",

39 };

40

41 struct mem {

42 char *memory;

43 size_t size;

44 };

45 struct mem chunk;

46

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

48

49 size_t realsize = size * nmemb;

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

51

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

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

54 printf(alloc_fail);

55 return 0;

56 }

57

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

59 mem->size += realsize;

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

61 return realsize;

62 }

63

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

65

66 CURL *curl_handle;

67 CURLcode res;

68

69 chunk.memory = malloc(1);

70 if (!chunk.memory) {

71 printf(alloc_fail);

72 return NULL;

73 }

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 for (i = 0; i < SIZEOF(paths); i++) {

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

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

134 if (f) break;

135 }

136 if (!f) return -1;

137

138 for (i = 0; 1; i++) {

139

140 struct symbol s = {0};

141 size_t len;

142

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

144

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

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

147

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

149

150 symbols = realloc(symbols, (i + 1) * sizeof(struct symbol));

151 if (!symbols) {

152 printf(alloc_fail);

153 return -1;

154 }

155 symbols[i] = s;

156 }

157 symbols_length = i;

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_name[] = "\"shortName\":\"";

166

167 static int find_copy(const char *haystack, const char *needle, size_t hay_len,

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

169

170 char *start, *end;

171

172 start = strnstr(haystack, needle, hay_len);

173 if (!start) return -1;

174

175 start += needle_len - 1;

176 end = start;

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

178

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

180

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

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

183

184 return 0;

185 }

186

187 static int update_symbol(struct symbol *symbol) {

188

189 char url[2048], buf[64], *data;

190 size_t len;

191 int ret = -1;

192

193 snprintf(url, sizeof(url), query_price, symbol->symbol);

194 data = handle_url(url, &len);

195 if (!data) return -1;

196

197 if (find_copy(data, str_price, len, sizeof(str_price), ',', buf,

198 sizeof(buf)))

199 goto clean;

200 symbol->price = atof(buf);

201

202 if (find_copy(data, str_old_price, len, sizeof(str_old_price), ',', buf,

203 sizeof(buf)))

204 goto clean;

205 symbol->previous_price = atof(buf);

206

207 if (find_copy(data, str_name, len, sizeof(str_name), '"',

208 symbol->name, sizeof(symbol->name)))

209 goto clean;

210

211 ret = 0;

212 clean:

213 free(data);

214

215 return ret;

216 }

217

218 void ansi_sleep(long micro) {

219 struct timeval tv;

220 tv.tv_sec = micro / 1000000;

221 tv.tv_usec = micro % 1000000;

222 select(0, NULL, NULL, NULL, &tv);

223 }

224

225 void *update_thread(void *ptr) {

226

227 int *run = ptr, interval, counter;

228

229 interval = 0;

230 while (*run) {

231 size_t i;

232 for (i = 0; i < symbols_length; i++) {

233 update_symbol(&symbols[i]);

234 counter = 0;

235 while (counter++ < interval * 10 && *run)

236 ansi_sleep(100000 / symbols_length);

237 if (!*run) break;

238 }

239 interval = INTERVAL;

240 }

241 return ptr;

242 }

243

244 #define COL_SYMBOL 2

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

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

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

248

249 int display(int *scroll) {

250

251 struct tb_event ev;

252 struct symbol symbol;

253 int i, w, h, bottom;

254

255 w = tb_width();

256 h = tb_height();

257

258 if ((size_t)h > symbols_length) *scroll = 0;

259

260 tb_clear();

261 for (i = 0; i < w; i++) tb_set_cell(i, 0, ' ', TB_BLACK, TB_WHITE);

262

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

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

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

266 tb_print(w + COL_VARIATION - 2, 0, TB_BLACK, TB_WHITE, "| Variation");

267

268 bottom = 1;

269 for (i = *scroll; i < (int)symbols_length; i++) {

270 int gain, j, y = i + 1;

271 symbol = symbols[i];

272 gain = (symbol.price >= symbol.previous_price);

273 tb_print(COL_SYMBOL, y - *scroll, TB_DEFAULT, TB_DEFAULT,

274 symbol.symbol);

275 tb_print(COL_NAME, y - *scroll, TB_DEFAULT, TB_DEFAULT,

276 symbol.name);

277

278 j = w + COL_PRICE - 2;

279 while (j++ < w)

280 tb_set_cell(j, y, ' ', TB_DEFAULT, TB_DEFAULT);

281 tb_printf(w + COL_PRICE, y - *scroll, TB_DEFAULT, TB_DEFAULT,

282 "%.2f", symbol.price);

283 tb_printf(w + COL_VARIATION + gain, y - *scroll,

284 gain ? TB_GREEN : TB_RED, TB_DEFAULT, "%.2f (%.2f%%)",

285 symbol.price - symbol.previous_price,

286 symbol.price / symbol.previous_price * 100 - 100);

287 if (y - *scroll >= h) {

288 bottom = 0;

289 break;

290 }

291 }

292

293 tb_present();

294

295 if (!tb_peek_event(&ev, REFRESH)) {

296 if (ev.key == TB_KEY_ESC || ev.ch == 'q') return -1;

297 if ((ev.key == TB_KEY_ARROW_DOWN || ev.ch == 'j') && !bottom)

298 (*scroll)++;

299 if ((ev.key == TB_KEY_ARROW_UP || ev.ch == 'k') && *scroll)

300 (*scroll)--;

301 }

302 return 0;

303 }

304

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

306

307 int scroll = 0, run;

308 pthread_t thread;

309

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

311

312 if (load_symbols()) {

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

314 return -1;

315 }

316

317 curl_global_init(CURL_GLOBAL_ALL);

318

319 if (tb_init()) {

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

321 return -1;

322 }

323

324 run = 1;

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

326

327 while (!display(&scroll)) ;

328

329 run = 0;

330 tb_shutdown();

331 pthread_join(thread, NULL);

332 curl_global_cleanup();

333 free(symbols);

334

335 return 0;

336 }

337