diff options
author | Antony Kellermann | 2018-02-16 12:18:11 -0500 |
---|---|---|
committer | Antony Kellermann | 2018-02-16 12:18:11 -0500 |
commit | 770288874b983861476b2865b1a9069e460e3228 (patch) | |
tree | 011a70a94d36f5d95cf21150665e12229fa2b4f4 | |
parent | 533381686ca8600b12af8c4e57608b42595abbc6 (diff) | |
download | aur-770288874b983861476b2865b1a9069e460e3228.tar.gz |
Reduced number of allocs and added error checking
-rw-r--r-- | .SRCINFO | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | PKGBUILD | 2 | ||||
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | api.c | 85 | ||||
-rw-r--r-- | api.h | 8 | ||||
-rw-r--r-- | main.c | 11 | ||||
-rw-r--r-- | portfolio.c | 62 | ||||
-rw-r--r-- | portfolio.h | 8 | ||||
-rw-r--r-- | tick.1 | 2 |
10 files changed, 113 insertions, 75 deletions
@@ -1,6 +1,6 @@ pkgbase = tick pkgdesc = Command line stock and cryptocurrency portfolio tracker. - pkgver = 1.7.1 + pkgver = 1.7.2 pkgrel = 1 url = https://github.com/aokellermann/tick arch = x86_64 @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -g -Wall --std=c99 +CFLAGS = -g -Wall --std=c99 -D_FORTIFY_SOURCE=2 -O2 OBJECTS = main.o api.o portfolio.o LIBS = -lcurl -ljson-c -lm BIN = tick @@ -1,7 +1,7 @@ # Maintainer: Antony Kellermann <aokellermann@gmail.com> pkgname=tick -pkgver=1.7.1 +pkgver=1.7.2 pkgrel=1 pkgdesc="Command line stock and cryptocurrency portfolio tracker." arch=('x86_64') diff --git a/README.md b/README.md index d189ee2ce6ef..d2a7cf6e00ff 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ license for more information. * Different ways to sort "check all" * Look for API to replace Morningstar for MUTF/OTCMKTS data, preferably with intraday data -* Encrypt data -* Debian/RPM package +* Encrypt data (rc4 from my github?) +* DEB/RPM package * List whether stock/etf/mutual fund/crypto/etc. in portfolio for less API calls -and portfolio distribution
\ No newline at end of file +and portfolio distribution (tried to implement, but IEX API is unpredictable; wait for v2)
\ No newline at end of file @@ -33,6 +33,8 @@ size_t api_string_writefunc(void* ptr, size_t size, size_t nmemb, String* hStrin String* api_curl_data(char* url, char* post_field) { String* pString = api_string_init(); + if (pString == NULL) + return NULL; CURL* curl = curl_easy_init(); CURLcode res; curl_global_init(CURL_GLOBAL_DEFAULT); @@ -77,16 +79,19 @@ double* api_get_current_price(char* ticker_name_string) { } double* iex_get_price(char* ticker_name_string) { - char* iex_api_string = calloc(64, sizeof(char)); - sprintf(iex_api_string, "%s%s%s", "https://api.iextrading.com/1.0/stock/", ticker_name_string, "/quote"); + char iex_api_string[64]; + sprintf(iex_api_string, "https://api.iextrading.com/1.0/stock/%s/quote", ticker_name_string); String* pString = api_curl_data(iex_api_string, NULL); - free(iex_api_string); if (strcmp(pString->data, "Unknown symbol") == 0) { //Invalid symbol api_string_destroy(&pString); return NULL; } Json* jobj = json_tokener_parse(pString->data); double* ret = malloc(sizeof(double) * 2); + if (ret == NULL) { + fprintf(stderr, "malloc() failed\n"); + exit(EXIT_FAILURE); + } char* price_string = (char*) json_object_to_json_string(json_object_object_get(jobj, "latestPrice")); char* close_price_string = (char*) json_object_to_json_string(json_object_object_get(jobj, "previousClose")); ret[0] = strtod(price_string, NULL); //Intraday current price @@ -97,36 +102,33 @@ double* iex_get_price(char* ticker_name_string) { } double* morningstar_get_price(char* ticker_name_string) { + char today_char[16], yesterday_char[16], morningstar_api_string[512]; time_t now = time(NULL); - struct tm* ts; - char* today_char = calloc(16, 1); - char* yesterday_char = calloc(16, 1); - ts = localtime(&now); + struct tm* ts = localtime(&now); mktime(ts); strftime(today_char, 16, "%Y-%m-%d", ts); ts->tm_mday -= 7; //get info from past 7 days mktime(ts); strftime(yesterday_char, 16, "%Y-%m-%d", ts); - char* morningstar_api_string = calloc(512, 1); - sprintf(morningstar_api_string, "%s%s%s%s|%s", - "http://globalquote.morningstar.com/globalcomponent/RealtimeHistoricalStockData.ashx?ticker=", - ticker_name_string, - "&showVol=true&dtype=his&f=d&curry=USD&isD=true&isS=true&hasF=true&ProdCode=DIRECT&range=", yesterday_char, - today_char); + sprintf(morningstar_api_string, + "http://globalquote.morningstar.com/globalcomponent/RealtimeHistoricalStockData.ashx?showVol=true&dtype=his&f=d&curry=USD&isD=true&isS=true&hasF=true&ProdCode=DIRECT&ticker=%s&range=%s|%s", + ticker_name_string, yesterday_char, today_char); String* pString = api_curl_data(morningstar_api_string, NULL); - free(morningstar_api_string); - free(today_char); - free(yesterday_char); - Json* jobj = json_tokener_parse(pString->data); if (strcmp("null", pString->data) == 0) { //Invalid symbol api_string_destroy(&pString); return NULL; } + Json* jobj = json_tokener_parse(pString->data); double* ret = malloc(sizeof(double) * 2); + if (ret == NULL) { + fprintf(stderr, "malloc() failed\n"); + exit(EXIT_FAILURE); + } Json* datapoints = json_object_object_get( json_object_array_get_idx(json_object_object_get(jobj, "PriceDataList"), 0), "Datapoints"); size_t size = json_object_array_length(datapoints); - Json* today = json_object_array_get_idx(json_object_array_get_idx(datapoints, size - 1), 0); //latest day of data will be in last array index + Json* today = json_object_array_get_idx(json_object_array_get_idx(datapoints, size - 1), + 0); //latest day of data will be in last array index char* price = (char*) json_object_to_json_string(today); ret[0] = strtod(price, NULL); //Last close price Json* yesterday = json_object_array_get_idx(json_object_array_get_idx(datapoints, size - 2), 0); @@ -138,10 +140,9 @@ double* morningstar_get_price(char* ticker_name_string) { } double* coinmarketcap_get_price(char* ticker_name_string) { - char* coinmarketcap_api_string = calloc(64, sizeof(char)); - sprintf(coinmarketcap_api_string, "%s%s", "https://api.coinmarketcap.com/v1/ticker/", ticker_name_string); + char coinmarketcap_api_string[64]; + sprintf(coinmarketcap_api_string, "https://api.coinmarketcap.com/v1/ticker/%s", ticker_name_string); String* pString = api_curl_data(coinmarketcap_api_string, NULL); - free(coinmarketcap_api_string); if (pString->data[0] == '{') { //Invalid symbol api_string_destroy(&pString); return NULL; @@ -153,6 +154,10 @@ double* coinmarketcap_get_price(char* ticker_name_string) { char* price = (char*) json_object_get_string(usd); char* change_1d = (char*) json_object_get_string(percent_change_1d); double* ret = malloc(sizeof(double) * 2); + if (ret == NULL) { + fprintf(stderr, "malloc() failed\n"); + exit(EXIT_FAILURE); + } ret[0] = strtod(price, NULL); //Current real-time price ret[1] = ret[0] - ((strtod(change_1d, NULL) / 100) * ret[0]); //Price 24 hours earlier api_string_destroy(&pString); @@ -162,24 +167,25 @@ double* coinmarketcap_get_price(char* ticker_name_string) { void news_print_top_three(char* ticker_name_string) { char* url_encoded_string = calloc(128, 1); + if (url_encoded_string == NULL) { + fprintf(stderr, "malloc() failed\n"); + return; + } for (int i = 0, j = 0; i < 128; i++, j++) { //Replace underscores and spaces with url encoded '%20's if (ticker_name_string[i] == '_' || ticker_name_string[i] == ' ') { memcpy(&url_encoded_string[i], "%20", 3); i += 2; } else url_encoded_string[i] = ticker_name_string[j]; } + char yearchar[16], news_api_string[256]; time_t now = time(NULL); - struct tm* ts; - char* yearchar = calloc(64, 1); - ts = localtime(&now); + struct tm* ts = localtime(&now); ts->tm_mday -= 14; // Lowerbound date is 14 days earlier mktime(ts); - strftime(yearchar, 64, "%Y-%m-%d", ts); - char* news_api_string = calloc(256, sizeof(char)); - sprintf(news_api_string, "%s&from=%s&q=%s", - "https://newsapi.org/v2/everything?sortBy=relevancy&pageSize=3&language=en&apiKey=1163c352d041460381f0a8273e60a9d1", + strftime(yearchar, 16, "%Y-%m-%d", ts); + sprintf(news_api_string, + "https://newsapi.org/v2/everything?sortBy=relevancy&pageSize=3&language=en&apiKey=1163c352d041460381f0a8273e60a9d1&from=%s&q=%s", yearchar, url_encoded_string); - free(yearchar); free(url_encoded_string); String* pString = api_curl_data(news_api_string, NULL); Json* jobj = json_tokener_parse(pString->data); @@ -187,14 +193,12 @@ void news_print_top_three(char* ticker_name_string) { 10) > 0) json_print_news(jobj); else printf("No articles. Try a different input.\n"); - free(news_api_string); api_string_destroy(&pString); json_object_put(jobj); } void json_print_news(Json* jobj) { - Json* article_list = json_object_object_get(jobj, "articles"); - Json* article; + Json* article_list = json_object_object_get(jobj, "articles"), * article; char* author_string, * title_string, * source_string, * date_string, * url_string; int results = (int) strtol(json_object_to_json_string(json_object_object_get(jobj, "totalResults")), NULL, 10); @@ -231,24 +235,25 @@ void json_print_news(Json* jobj) { } const char* google_shorten_link(char* url_string) { - char* google_api_string = "https://www.googleapis.com/urlshortener/v1/url?key=AIzaSyAoMAvMPpc7U8lfrnGMk2ZKl966tU2pppU"; - char* post_string = calloc(1024, 1); + char post_string[1024], copy[1024]; sprintf(post_string, "{\"longUrl\": \"%s\"}", url_string); //Format HTTP POST - String* pString = api_curl_data(google_api_string, post_string); - free(post_string); + String* pString = api_curl_data( + "https://www.googleapis.com/urlshortener/v1/url?key=AIzaSyAoMAvMPpc7U8lfrnGMk2ZKl966tU2pppU", post_string); Json* jobj = json_tokener_parse(pString->data); Json* short_url = json_object_object_get(jobj, "id"); const char* short_url_string = json_object_to_json_string(short_url); - char* copy = calloc(1024, 1); strcpy(copy, short_url_string); char* final = calloc(strlen(copy) + 1, 1); + if (final == NULL) { + fprintf(stderr, "calloc() failed\n"); + exit(EXIT_FAILURE); + } for (int i = 0, j = 0; j < strlen(copy); i++, j++) { //Ignore escape characters if (copy[j] == '\\' || copy[j] == '\"') j++; final[i] = copy[j]; } json_object_put(jobj); - free(copy); api_string_destroy(&pString); return final; } @@ -256,6 +261,10 @@ const char* google_shorten_link(char* url_string) { const char* strip_char(char* string, char c) { size_t len = strlen(string); char* final_string = calloc(len + 1, 1); + if (final_string == NULL) { + fprintf(stderr, "calloc() failed\n"); + exit(EXIT_FAILURE); + } for (int i = 0, j = 0; j < (int) len; i++, j++) { while (string[j] == c) j++; @@ -49,7 +49,7 @@ String* api_curl_data(char* url, char* post_field); * 2. Morningstar -- MUTF/OTCMKTS * 3. Coinmarketcap -- CRYPTO * @param ticker_name_string symbol - * @return price data + * @return price data or NULL if invalid symbol */ double* api_get_current_price(char* ticker_name_string); @@ -63,7 +63,7 @@ size_t api_string_writefunc(void* ptr, size_t size, size_t nmemb, String* hStrin * Returns current and yesterday's price of a stock with data from IEX. * Tested for NASDAQ, NYSE, and NYSEARCA listed stocks/ETFs. * @param ticker_name_string symbol - * @return current price of stock + * @return price data as defined by api_get_current_price or NULL if invalid symbol */ double* iex_get_price(char* ticker_name_string); @@ -72,7 +72,7 @@ double* iex_get_price(char* ticker_name_string); * Tested for MUTF and OTCMKTS listed securities. * @param ticker_name_string symbol * @param offset number of days ago to get price of (0 = today) - * @return price of security + * @return price data as defined by api_get_current_price or NULL if invalid symbol */ double* morningstar_get_price(char* ticker_name_string); @@ -80,7 +80,7 @@ double* morningstar_get_price(char* ticker_name_string); * Returns current and yesterday's price of a cryptocurrency with data from Coinmarketcap. * All cryptocurrencies listed on Coinmarketcap will work. * @param ticker_name_string symbol - * @return current price of cryptocurrency + * @return price data as defined by api_get_current_price or NULL if invalid symbol */ double* coinmarketcap_get_price(char* ticker_name_string); @@ -30,7 +30,7 @@ int main(int argc, char* argv[]) { return 0; } - //Convert legacy porfolio + // Convert legacy porfolio if (argc == 2 && strcmp(argv[1], "convert") == 0) { portfolio_legacy_convert(); free((void*) portfolio_file); @@ -59,8 +59,11 @@ int main(int argc, char* argv[]) { strtoupper(sym); if (strcmp(sym, "ALL") == 0) portfolio_print_all(fp); - else - free(portfolio_print_stock(sym, fp, NULL)); + else { + double* data = portfolio_print_stock(sym, fp, NULL); + if (data != NULL) + free(data); + } } } @@ -99,7 +102,7 @@ int main(int argc, char* argv[]) { // If last two characters of price are "EA", calculate // total price with number of units char ea = 0; - if (susd[ulen - 2] == 'E' && susd[ulen - 1] == 'A') { + if (ulen > 2 && susd[ulen - 2] == 'E' && susd[ulen - 1] == 'A') { ea = 1; susd[ulen - 2] = '\0'; } diff --git a/portfolio.c b/portfolio.c index 8888ebff2def..d95ac178d874 100644 --- a/portfolio.c +++ b/portfolio.c @@ -3,8 +3,11 @@ void portfolio_file_init() { char* home = getenv("HOME"); char* path = (char*) malloc(strlen(home) + 30); - memcpy(path, home, strlen(home) + 1); - memcpy(&path[strlen(home)], "/.tick_portfolio.json", 25); + if (path == NULL) { + fprintf(stderr, "malloc() failed\n"); + exit(EXIT_FAILURE); + } + sprintf(path, "%s/.tick_portfolio.json", home); portfolio_file = path; } @@ -13,7 +16,8 @@ char* portfolio_file_get_string(FILE* fp) { fseek(fp, 0, SEEK_END); size_t portfolio_size = (size_t) ftell(fp); fseek(fp, 0, SEEK_SET); - fread(portfolio_string, portfolio_size, sizeof(char), fp); + if (fread(portfolio_string, sizeof(char), portfolio_size, fp) != portfolio_size) + return NULL; return portfolio_string; } @@ -27,6 +31,10 @@ void portfolio_modify(char* ticker_name_string, double quantity_shares, double u return; } char* portfolio_string = portfolio_file_get_string(fp); + if (portfolio_string == NULL) { + fprintf(stderr, "malloc() failed\n"); + return; + } Json* jobj = NULL; if (strcmp(portfolio_string, "") == 0) { //new file jobj = json_object_new_array(); @@ -41,23 +49,27 @@ void portfolio_modify(char* ticker_name_string, double quantity_shares, double u json_object_put(jobj); return; } - if (strcmp("USD$", ticker_name_string) != 0 && api_get_current_price(ticker_name_string) == NULL) { - printf("Invalid symbol.\n"); - json_object_put(jobj); - free(portfolio_string); - return; + if (strcmp("USD$", ticker_name_string) != 0) { + double* data = api_get_current_price(ticker_name_string); // Curl data and check if NULL + if (data == NULL) { + printf("Invalid symbol.\n"); + json_object_put(jobj); + free(portfolio_string); + return; + } else free(data); } - Json* new_object = json_object_new_object(); //creates new array index and adds values to it + Json* new_object = json_object_new_object(); // Creates new array index and adds values to it json_object_array_add(jobj, new_object); json_object_object_add(new_object, "Symbol", json_object_new_string(ticker_name_string)); json_object_object_add(new_object, "Shares", json_object_new_double(quantity_shares)); json_object_object_add(new_object, "USD_Spent", json_object_new_double(usd_spent)); printf("Added %lf %s bought for %lf to portfolio.\n", quantity_shares, ticker_name_string, usd_spent); - } else { + } else { //if already in portfolio Json* current_index = json_object_array_get_idx(jobj, (size_t) index); - double current_shares = json_object_get_double(json_object_object_get(current_index, "Shares")); + double current_shares = json_object_get_double( + json_object_object_get(current_index, "Shares")); // Stores values already there double current_spent = json_object_get_double(json_object_object_get(current_index, "USD_Spent")); - json_object_object_del(current_index, "Shares"); + json_object_object_del(current_index, "Shares"); // Deletes the objects already there json_object_object_del(current_index, "USD_Spent"); if (option == SET) { current_shares = quantity_shares; @@ -71,7 +83,7 @@ void portfolio_modify(char* ticker_name_string, double quantity_shares, double u } else { current_shares -= quantity_shares; current_spent -= usd_spent; - if (current_shares < 0 || current_spent < 0) { + if (current_shares < 0 || current_spent < 0) { // If you try to remove more than you have printf("You do not have that many %s to remove.\n", ticker_name_string); json_object_put(jobj); free(portfolio_string); @@ -128,6 +140,7 @@ double* portfolio_print_stock(char* ticker_name_string, FILE* fp, Json* current_ */ double* data = malloc(sizeof(double) * 3); char* portfolio_string = NULL; + Json* jobj = NULL; if (fp == NULL) { //if being called from portfolio_print_all ticker_name_string = (char*) strip_char( (char*) json_object_get_string(json_object_object_get(current_index, "Symbol")), '\"'); @@ -135,7 +148,12 @@ double* portfolio_print_stock(char* ticker_name_string, FILE* fp, Json* current_ data[1] = json_object_get_double(json_object_object_get(current_index, "USD_Spent")); } else { //if being called directly from main portfolio_string = portfolio_file_get_string(fp); - Json* jobj = json_tokener_parse(portfolio_string); + if (portfolio_string == NULL) { + fprintf(stderr, "fread() failed\n"); + free(data); + return NULL; + } + jobj = json_tokener_parse(portfolio_string); int index = portfolio_symbol_index(ticker_name_string, jobj); if (index == -1) { printf("You do not have %s in your portfolio.\n", ticker_name_string); @@ -160,10 +178,10 @@ double* portfolio_print_stock(char* ticker_name_string, FILE* fp, Json* current_ data[0] / ticker_current_price_usd, ticker_name_string, data[0], data[1], data[0] - data[1], (100 * (data[0] - data[1])) / data[1], data[2], ticker_1d_percent_change); - if (fp == NULL) { + if (fp == NULL) free(ticker_name_string); - free(portfolio_string); - } + else json_object_put(jobj); + free(portfolio_string); return data; } @@ -198,14 +216,20 @@ char* portfolio_legacy_get_next_val(FILE* fp) { void portfolio_legacy_convert() { printf("Warning: this will overwrite your JSON formatted portfolio, which is stored at: \"$HOME/.tick_portfolio\". Continue? y/n\n"); char c = 0; - while (c != 'y' && c != 'n') - scanf("%c", &c); + while (c != 'y' && c != 'n') { + if (scanf("%c", &c) < 0) + c = 0; + } if (c == 'n') { printf("Aborted.\n"); return; } FILE* fp = fopen(portfolio_file, "w"); char* pf = portfolio_file_get_string(fp); + if (pf == NULL) { + fprintf(stderr, "fread() failed\n"); + exit(EXIT_FAILURE); + } if (strcmp(pf, "") != 0) remove(portfolio_file); fclose(fp); diff --git a/portfolio.h b/portfolio.h index a59584ad12b8..ab9f02a45a8b 100644 --- a/portfolio.h +++ b/portfolio.h @@ -1,9 +1,6 @@ #ifndef PORTFOLIO_H #define PORTFOLIO_H -#include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <math.h> #include "api.h" @@ -18,6 +15,11 @@ const char* portfolio_file; */ void portfolio_file_init(); +/** + * Stores the given file in a string and returns it + * @param fp the file + * @return the string containing the file + */ char* portfolio_file_get_string(FILE* fp); /** @@ -1,4 +1,4 @@ -.TH TICK "1" "January 2018" "Tick 1.7.0" "User Commands" +.TH TICK "1" "January 2018" "Tick 1.7.2" "User Commands" .SH NAME Tick - Command line stock and cryptocurrency portfolio tracker. |