diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/globals.h | 33 | ||||
| -rw-r--r-- | src/main.c | 219 |
2 files changed, 252 insertions, 0 deletions
diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 0000000..7e2bf6c --- /dev/null +++ b/src/globals.h @@ -0,0 +1,33 @@ +#include <stdbool.h> + +#define ARRAYLEN(x) (sizeof(x) / sizeof(x[0])) + +typedef unsigned long long ullong; +typedef unsigned long ulong; +typedef unsigned uint; +typedef unsigned short ushort; +typedef unsigned char uchar; + +struct date_t { + ushort year; + ushort day; /* 0 = 1st jan, 364 = 31st dec */ +}; + +/* money is represented internally as dollars + cents */ +struct money_t { + ullong cents; +}; + +struct stock_t { + char *symbol; + char *fullname; + struct money_t bought_for; + ulong count; + struct money_t price; +}; + +struct player_t { + struct money_t cash; + struct stock_t *portfolio; + uint portfolio_len; +}; diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..6902b83 --- /dev/null +++ b/src/main.c @@ -0,0 +1,219 @@ +#include "globals.h" + +#include <curl/curl.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void cleanup(void) +{ + curl_global_cleanup(); +} + +struct data_buffer_t { + char *data; + uint back; +}; + +size_t curl_callback(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + struct data_buffer_t *buf = userdata; + + buf->data = realloc(buf->data, buf->back + size * nmemb); + + memcpy(buf->data + buf->back, ptr, size * nmemb); + + buf->back += size * nmemb; + + return CURLE_OK; +} + +bool get_stock_info(char *symbol, struct money_t *price, char **name_ret) +{ + CURL *curl = curl_easy_init(); + if(!curl) + { + return false; + } + + char url[256]; + snprintf(url, sizeof(url), "http://download.finance.yahoo.com/d/quotes.csv?s=%s&f=nl1&e=.csv", symbol); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + struct data_buffer_t buf; + memset(&buf, 0, sizeof(buf)); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback); + + CURLcode res = curl_easy_perform(curl); + + /** now parse the data **/ + + /* the stock name is in quotes, find it! */ + + uint name_len = 0; + for(uint i = 1; i < buf.back; ++i) + { + if(buf.data[i] == '"') + break; + ++name_len; + } + + const uint name_offs = 1; + uint price_offs = name_len + 3; + uint price_len = buf.back - price_offs; + + char *name = malloc(name_len + 1); + memcpy(name, buf.data + name_offs, name_len); + name[name_len] = '\0'; + + *name_ret = name; + + /* get price */ + + char *pricebuf = malloc(price_len + 1); + memcpy(pricebuf, buf.data + price_offs, price_len); + pricebuf[price_len] = '\0'; + + free(buf.data); + + /* remove the decimal point */ + + for(int i = 0; i < price_len; ++i) + { + if(pricebuf[i] == '.') + memmove(pricebuf + i, pricebuf + i + 1, price_len - i); + } + + price->cents = strtoull(pricebuf, NULL, 10); + + free(pricebuf); + + return true; +} + +void buy_handler(struct player_t *player) +{ + char sym[16]; + printf("Enter the ticker symbol of the stock you wish to purchase: "); + scanf("%15s", sym); + struct money_t price; + printf("Getting stock information...\n"); + char *name; + if(!get_stock_info(sym, &price, &name)) + { + printf("Failed to get query information for '%s'\n", sym); + } + printf("Stock name: %s\n", name); + printf("Price per share: $%d.%d\n", price.cents / 100, price.cents % 100); +} + +void sell_handler(struct player_t *player) +{ +} + +void quit_handler(struct player_t *player) +{ + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + atexit(cleanup); + + curl_global_init(CURL_GLOBAL_DEFAULT); + + struct player_t *player = malloc(sizeof(struct player_t)); + memset(player, 0, sizeof(struct player_t)); + + while(1) + { + printf("Your portfolio:\n"); + printf("===============\n"); + for(int i = 0; i < player->portfolio_len; ++i) + { + struct stock_t *stock = player->portfolio + i; + printf("%5s %20s $%d.%d\n", stock->symbol, stock->fullname, stock->price.cents / 100, stock->price.cents % 100); + } + printf("===============\n"); + + printf("Current cash: $%d.%d\n", player->cash.cents / 100, player->cash.cents % 100); + + struct command_t { + const char *name; + const char *command; + void (*handler)(struct player_t*); + }; + + const struct command_t commands[] = { + { "[B]uy", "buy", buy_handler }, + { "[S]ell", "sell", sell_handler }, + { "[Q]uit", "quit", quit_handler } + }; + + for(uint i = 0; i < ARRAYLEN(commands); ++i) + { + printf("%d. %s\n", i + 1, commands[i].name); + } + + printf("What would you like to do? "); + char cmdbuf[32]; + scanf("%31s", cmdbuf); + + /* find the best command */ + + int best_command = -1; + + /* first, search for an exact match */ + for(int i = 0; i < ARRAYLEN(commands); ++i) + { + if(strcmp(cmdbuf, commands[i].command) == 0) + { + best_command = i; + goto exec_cmd; + } + } + + /* now look for a partial match */ + for(int i = 0; i < ARRAYLEN(commands); ++i) + { + int len = strlen(cmdbuf); + if(len > strlen(commands[i].command)) + continue; + for(int j = 1; j <= len; ++j) + { + char *buf1 = malloc(j + 1); + memset(buf1, 0, j + 1); + memcpy(buf1, cmdbuf, j); + buf1[j] = '\0'; + + char *buf2 = malloc(j + 1); + memset(buf2, 0, j + 1); + memcpy(buf2, commands[i].command, j); + buf2[j] = '\0'; + + if(strcmp(buf1, buf2) == 0) + { + best_command = i; + free(buf1); + free(buf2); + goto exec_cmd; + } + else + { + free(buf1); + free(buf2); + } + } + } + + exec_cmd: + + if(best_command >= 0) + commands[best_command].handler(player); + } +} |