summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Kaivo <jkk@ung.org>2022-04-13 19:42:10 -0400
committerJakob Kaivo <jkk@ung.org>2022-04-13 19:42:10 -0400
commitefeeb5db9e8178a5f0ade3f99832782ab0cb4442 (patch)
tree282b4821ecce8e184b7d7f0ce5edd8e52d529839
parent31f030bb17c5147850e764267227ecfd0431df66 (diff)
actually do some of the things
-rw-r--r--src/monetary/strfmon.c131
1 files changed, 89 insertions, 42 deletions
diff --git a/src/monetary/strfmon.c b/src/monetary/strfmon.c
index cf88a9ba..b66074d2 100644
--- a/src/monetary/strfmon.c
+++ b/src/monetary/strfmon.c
@@ -1,8 +1,10 @@
#include <monetary.h>
#include <ctype.h>
+#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
ssize_t strfmon(char * restrict s, size_t maxsize, const char * restrict format, ...)
{
@@ -10,6 +12,8 @@ ssize_t strfmon(char * restrict s, size_t maxsize, const char * restrict format,
va_list ap;
va_start(ap, format);
+ struct lconv *lc = localeconv();
+
while (*format && i < maxsize) {
if (*format != '%') {
s[i++] = *format++;
@@ -25,68 +29,111 @@ ssize_t strfmon(char * restrict s, size_t maxsize, const char * restrict format,
}
char fill = ' ';
- char style = '+';
+ char positive_negative_style = '+';
+ char *positive_sign = lc->positive_sign;
+ char *negative_sign = lc->negative_sign;
+ char *negative_end = "";
+ char *symbol = lc->currency_symbol;
int grouping = 1;
- int symbol = 1;
+
enum { left, right } align = right;
- switch (*format) {
- case '=':
- fill = format[1];
- format += 2;
- break;
- case '^':
- grouping = 0;
- format++;
- break;
- case '+':
- case '(':
- style = *format;
- format++;
- break;
- case '!':
- symbol = 0;
- format++;
- break;
- case '-':
- align = left;
+ while (strchr("=^+(!-", *format)) {
+ switch (*format) {
+ case '=':
+ fill = format[1];
+ format++;
+ break;
+
+ case '^':
+ grouping = 0;
+ break;
+
+ case '+':
+ /* is default */
+ break;
+
+ case '(':
+ positive_sign = "";
+ negative_sign = "(";
+ negative_end = ")";
+ break;
+
+ case '!':
+ symbol = "";
+ break;
+
+ case '-':
+ align = left;
+ break;
+ }
format++;
- break;
}
- unsigned long width = 0;
+ int width = 0;
if (isdigit(*format)) {
char *end = NULL;
- width = strtoul(format, &end, 10);
- format = end + 1;
+ width = (int)strtol(format, &end, 10);
+ format = end;
}
+ int lprecision = 0;
if (*format == '#') {
- /* TODO: left precision */
+ if (!isdigit(format[1])) {
+ va_end(ap);
+ return -1;
+ }
+ char *end = NULL;
+ lprecision = (int)strtol(format + 1, &end, 10);
+ format = end;
}
+ int rprecision = lc->frac_digits;
if (*format == '.') {
- /* TODO: right precision */
+ if (!isdigit(format[1])) {
+ va_end(ap);
+ return -1;
+ }
+ char *end = NULL;
+ rprecision = (int)strtol(format + 1, &end, 10);
+ format = end;
}
- if (*format == 'i') {
- /* international format */
- double value = va_arg(ap, double);
- i += snprintf(s + i, maxsize - i, "USD %.2g", value);
- format++;
- continue;
+ if (*format != 'i' && *format != 'n') {
+ va_end(ap);
+ return -1;
}
- if (*format == 'n') {
- /* national format */
- double value = va_arg(ap, double);
- i += snprintf(s + i, maxsize - i, "$%.2g", value);
- format++;
- continue;
+ /* i == international format */
+ /* n == national format */
+
+ double value = va_arg(ap, double);
+ int negative = 0;
+ if (value < 0) {
+ negative = 1;
+ value = value * -1;
}
- va_end(ap);
- return -1;
+ char *currency = lc->currency_symbol;
+
+ char *sign = negative ? negative_sign : positive_sign;
+
+ i += snprintf(s + i, maxsize - i, "%s", sign);
+ i += snprintf(s + i, maxsize - i, "%s", symbol);
+
+ char valuebuf[64];
+ snprintf(valuebuf, sizeof(valuebuf), "%'*.2f", width, value);
+ for (char *s = valuebuf; *s == ' '; s++) {
+ *s = fill;
+ }
+
+ i += snprintf(s + i, maxsize - i, "%s", valuebuf);
+
+ if (negative) {
+ i += snprintf(s + i, maxsize - i, "%s", negative_end);
+ }
+
+ format++;
}
va_end(ap);