commit f36a9b00099e629fd71eecc445932e29f85da66a
parent 331d704f6277164bc4167bcc50a24c9de3e234ee
Author: William Casarin <jb55@jb55.com>
Date: Sat, 23 Dec 2017 14:10:58 -0800
unit: implement arbitrary units per BTC
Handy for comparing against fiat currencies.
Examples
--------
$ bcalc -p --price 16000 --usd 670 bits
10.72 USD
$ bcalc -p --price 20000 --bits 20 usd
1000 bits
Closes #9
Diffstat:
7 files changed, 82 insertions(+), 6 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,7 +3,7 @@ BIN=bcalc
DEPS=$(wildcard deps/*/*.c) $(GEN)
PREFIX ?= /usr/local
-CFLAGS = -std=c99
+CFLAGS = -O0 -g -std=c99
SRC = num.c
DEPS = $(wildcard deps/*/*.c) $(SRC)
OBJS = $(DEPS:.c=.o) parser.tab.o lex.yy.o
@@ -25,6 +25,9 @@ install: $(BIN)
test: $(BIN) fake
@sh -c "cd test && ./run"
+TAGS: fake
+ etags -o - *.c > $@
+
$(BIN): $(OBJS) bcalc.c num.h
$(CC) $(CFLAGS) -Ideps -o $@ bcalc.c $(OBJS)
diff --git a/bcalc.c b/bcalc.c
@@ -9,6 +9,8 @@ extern int yylex();
extern int yyparse();
extern enum unit g_output_format;
extern int g_print_unit;
+struct num g_other;
+char *g_other_name = "other";
void yyerror(const char* s);
@@ -31,6 +33,34 @@ format_setting(bits, UNIT_BITS)
format_setting(finney, UNIT_FINNEY)
format_setting(sat, UNIT_SATOSHI)
format_setting(msat, UNIT_MSATOSHI)
+format_setting(optother, UNIT_OTHER)
+
+static void optusd(command_t *self) {
+ struct settings *set = (struct settings*)self->data;
+ set->format = UNIT_OTHER;
+ g_other_name = "USD";
+}
+
+static void
+setprice(command_t *cmd) {
+ const char *p = cmd->arg;
+ char *endptr;
+ g_other.unit = UNIT_OTHER;
+ g_other.type = TYPE_INT;
+ g_other.intval = strtoull(p, &endptr, 10);
+
+ // float?
+ if (endptr) {
+ if (*endptr == '.') {
+ g_other.floatval = atof(p);
+ g_other.type = TYPE_FLOAT;
+ if (g_other.floatval == 0) {
+ fprintf(stderr, "error: invalid --price value '%s'", p);
+ exit(1);
+ }
+ }
+ }
+}
char *
join(char *strs[], int len, char *sep) {
@@ -62,6 +92,7 @@ int main(int argc, char *argv[argc]) {
int yybuffer;
struct settings settings = { .print_unit = 0, .format = UNIT_SATOSHI };
cmd.data = (void*)&settings;
+ g_other.unit = UNIT_NONE;
command_init(&cmd, argv[0], "0.0.1");
@@ -71,6 +102,9 @@ int main(int argc, char *argv[argc]) {
command_option(&cmd, "-f", "--finney", "output finneys", finney);
command_option(&cmd, "-s", "--sat", "output satoshis (default)", sat);
command_option(&cmd, "-m", "--msat", "output millisatoshis", msat);
+ command_option(&cmd, "-P", "--price <arg>", "set price for arbitrary unit per BTC", setprice);
+ command_option(&cmd, "-o", "--other", "output arbitrary unit, set by --price", optother);
+ command_option(&cmd, "-u", "--usd", "output arbitrary usd units", optusd);
command_option(&cmd, "-p", "--print-unit", "output the selected unit at the end",
print_unit);
diff --git a/lexer.l b/lexer.l
@@ -25,6 +25,11 @@
return T_UNIT;
}
+fiat|FIAT|alt|ALT|usd|USD|cur|CUR|other|OTHER {
+ yylval.unit = UNIT_OTHER;
+ return T_UNIT;
+}
+
[fF][iI][nN][nN][eE][yY]?[sS]? {
yylval.unit = UNIT_FINNEY;
return T_UNIT;
diff --git a/num.c b/num.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <assert.h>
+#include <stdlib.h>
#include <math.h>
#include <inttypes.h>
#include <string.h>
@@ -13,11 +14,23 @@ num_init(struct num *num) {
num->intval = 0LL;
}
+void
+error_any() {
+ fprintf(stderr, "error: --price argument required when using arbitrary units\n");
+ exit(1);
+}
+
static int64_t
num_to_msat(struct num *num) {
+ double anyval = 1.0;
+
if (num->type == TYPE_FLOAT) {
double val = num->floatval;
switch (num->unit) {
+ case UNIT_OTHER:
+ if (g_other.unit == UNIT_NONE) error_any();
+ return (int64_t)val * (g_other.type == TYPE_FLOAT ? g_other.floatval
+ : (double)g_other.intval);
case UNIT_MSATOSHI:
return (int64_t)val;
case UNIT_SATOSHI:
@@ -47,8 +60,12 @@ num_to_msat(struct num *num) {
return val * BITS;
case UNIT_MBTC:
return val * MBTC;
+ case UNIT_OTHER:
+ if (g_other.unit != UNIT_OTHER) error_any();
+ anyval = g_other.type == TYPE_FLOAT?
+ g_other.floatval : (double)g_other.intval;
case UNIT_BTC:
- return val * BTC;
+ return (val/anyval) * BTC;
case UNIT_NONE:
assert(!"got UNIT_NONE in num_to_msat");
}
@@ -79,6 +96,11 @@ unit_msat_multiple(enum unit format) {
case UNIT_FINNEY: return FINNEY;
case UNIT_BITS: return BITS;
case UNIT_MBTC: return MBTC;
+ case UNIT_OTHER:
+ if (g_other.unit != UNIT_OTHER) error_any();
+ return BTC / (g_other.type == TYPE_FLOAT
+ ? g_other.floatval
+ : g_other.intval);
case UNIT_BTC: return BTC;
case UNIT_NONE: assert(!"got UNIT_NONE in num_to_msat");
}
@@ -193,6 +215,9 @@ unit_name(enum unit unit) {
case UNIT_BITS: return "bits";
case UNIT_MBTC: return "mBTC";
case UNIT_BTC: return "BTC";
+ case UNIT_OTHER: return g_other_name;
case UNIT_NONE: assert(!"got UNIT_NONE in num_to_msat");
+ default:
+ assert(!"missing unit");
}
}
diff --git a/num.h b/num.h
@@ -19,6 +19,7 @@ enum unit {
UNIT_FINNEY,
UNIT_SATOSHI,
UNIT_MSATOSHI,
+ UNIT_OTHER,
UNIT_NONE,
};
@@ -31,13 +32,15 @@ struct num
{
enum num_type type;
enum unit unit;
+ const char *unitstr;
union {
int64_t intval;
double floatval;
};
};
-
+extern struct num g_other;
+extern char *g_other_name;
void num_add(struct num *dst, struct num *a, struct num *b);
void num_sub(struct num *dst, struct num *a, struct num *b);
diff --git a/test/run b/test/run
@@ -1,13 +1,11 @@
#!/usr/bin/env bash
-set -e
-
n=1
c=$(($(wc -l < tests.csv) - 1))
printf "1..%d\n" "$c"
-tail -n+2 tests.csv | \
+tail -n+2 tests.csv | sed -n '/^# failing/q;p' | \
(fail=0
while IFS=, read -r description args input expected
do
diff --git a/test/tests.csv b/test/tests.csv
@@ -18,3 +18,11 @@ satoshi plural,-sp,10 sats,10 sat
msat singular,-mp,1 msat,1 msat
msat plural,-mp,1 msats,1 msat
arg tokens work,-p --mbtc 1 BTC,,1000 mBTC
+simple fiat test,-p --price 15000 --msat,1 fiat,6666666 msat
+simple fiat test with bits,-p --price 15000 --bits,1 fiat,66.66666 bits
+1 usd is 1 BTC,-p --price 1 --btc,1 usd,1 BTC
+1 BTC is 10 usd,-p --price 10 --usd,1 btc,10 USD
+1 BTC is 1000000 other,-p --price 1000000 --other,100 mbtc,100000 other
+1 other @15k is 66.66666 bits,-p --price 15000 --bits,1 other,66.66666 bits
+# failing
+1 BTC is 15000 usd,-p --price 15000 --usd,1 btc,15000 USD