aboutsummarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntony Kellermann2018-02-16 12:18:11 -0500
committerAntony Kellermann2018-02-16 12:18:11 -0500
commit770288874b983861476b2865b1a9069e460e3228 (patch)
tree011a70a94d36f5d95cf21150665e12229fa2b4f4
parent533381686ca8600b12af8c4e57608b42595abbc6 (diff)
downloadaur-770288874b983861476b2865b1a9069e460e3228.tar.gz
Reduced number of allocs and added error checking
-rw-r--r--.SRCINFO2
-rw-r--r--Makefile2
-rw-r--r--PKGBUILD2
-rw-r--r--README.md6
-rw-r--r--api.c85
-rw-r--r--api.h8
-rw-r--r--main.c11
-rw-r--r--portfolio.c62
-rw-r--r--portfolio.h8
-rw-r--r--tick.12
10 files changed, 113 insertions, 75 deletions
diff --git a/.SRCINFO b/.SRCINFO
index e69c78bdf892..ad99102e6bc9 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -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
diff --git a/Makefile b/Makefile
index bcacc35532c0..ffb7da6f136d 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/PKGBUILD b/PKGBUILD
index 761047e0d487..41724ca628d8 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -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
diff --git a/api.c b/api.c
index cd78c9923bcc..2fc7ec17c9c8 100644
--- a/api.c
+++ b/api.c
@@ -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++;
diff --git a/api.h b/api.h
index e11523e0d3fd..365525756faa 100644
--- a/api.h
+++ b/api.h
@@ -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);
diff --git a/main.c b/main.c
index 67d8cd4c9101..1d5c0d7f7402 100644
--- a/main.c
+++ b/main.c
@@ -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);
/**
diff --git a/tick.1 b/tick.1
index a1fa83171a7d..fa70af8e0bcc 100644
--- a/tick.1
+++ b/tick.1
@@ -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.