#include #include #include #include #include extern const char *__progname; #include "currlib.h" static CURR *currencies = 0; static void (*errfn)(int, const char *); static jmp_buf err_jmp; static char errbuf[10240]; static int fmt_neg; static unsigned long int fmt_i; static int fmt_fplaces; static unsigned long int fmt_f; static CURR unk_curr; static void init_unk_curr(void) { unk_curr.name = "?"; unk_curr.places = 0; unk_curr.divisor = 1; unk_curr.fmt = "?¤%?--%.%i"; } static unsigned long int tentothe(unsigned int n) { unsigned int i; unsigned long int p; p = 1; for (i=1;i&&(i<=n);i<<=1) ; if (! i) return(~0UL); for (i>>=1;i>=1;i>>=1) { p *= p; if (n & i) p *= 10; } return(p); } static volatile void err(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vsprintf(&errbuf[0],fmt,ap); va_end(ap); (*errfn)(1,&errbuf[0]); longjmp(err_jmp,1); } static void warn(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vsprintf(&errbuf[0],fmt,ap); va_end(ap); (*errfn)(0,&errbuf[0]); } static void default_errf(int fatal, const char *msg) { fprintf(stderr,"%s: %s\n",__progname,msg); if (fatal) exit(1); } void load_currencies(const char *fn, void (*errf)(int, const char *)) { FILE *f; char *line; int have; int len; int ch; CURR *c; char *c1; char *c2; errfn = errf ? errf : default_errf; if (setjmp(err_jmp)) return; f = fopen(fn,"r"); if (f == 0) { err("can't open currencies file %s",fn); } currencies = 0; while (1) { have = 8; len = 0; line = malloc(8+1); while (1) { ch = getc(f); if (ch == EOF) { if (len > 0) { warn("partial line at end of currencies file %s ignored\n",fn); } goto out; /* break 2 */ } if (ch == '\n') { line[len] = '\0'; break; } while (have <= len) line = realloc(line,(have+=8)+1); line[len++] = ch; } if (0) { badcurr:; warn("bad line in currencies file %s: %s",fn,line); continue; } c1 = index(line,':'); if (c1 == 0) goto badcurr; c2 = index(c1+1,':'); if (c2 == 0) goto badcurr; *c1 = '\0'; c = malloc(sizeof(CURR)); c->name = line; c->places = atoi(c1+1); c->divisor = tentothe(c->places); c->fmt = c2 + 1; c->link = currencies; currencies = c; } out:; fclose(f); } void fake_currencies(void) { currencies = malloc(sizeof(CURR)); currencies->link = 0; currencies->name = ""; currencies->places = 0; currencies->divisor = 1; currencies->fmt = "¤%?--%.%i"; } CURR *find_currency(const char *name) { CURR *c; for (c=currencies;c;c=c->link) { if (!strcmp(name,c->name)) return(c); } return(0); } CURR *walk_currencies(int (*fn)(CURR *)) { CURR *c; for (c=currencies;c;c=c->link) { if ((*fn)(c)) return(c); } return(0); } static int do_format(char *buf, const char *fmt, int (*ifn)(char *), int (*ffn)(char *)) { typedef struct istack ISTACK; struct istack { ISTACK *link; unsigned int true:1; }; static ISTACK *istack = 0; ISTACK *it; int cond; int n; int d; #define INTRUEIF() (!istack || istack->true) while (istack) { it = istack; istack = it->link; free(it); } n = 0; for (;*fmt;fmt++) { if (*fmt != '%') { if (INTRUEIF()) { if (buf) *buf++ = *fmt; n ++; } continue; } fmt ++; switch (*fmt) { case '\0': if (buf) { bcopy("%EOS",buf,4); buf += 4; } n += 4; goto out; /* break 2 */ break; default: if (buf) { *buf++ = '%'; *buf++ = '<'; *buf++ = '?'; *buf++ = *fmt; *buf++ = '>'; } n += 5; continue; break; case 'i': if (INTRUEIF()) { d = (*ifn)(buf); if (buf) buf += d; n += d; } break; case 'f': if (INTRUEIF()) { d = (*ffn)(buf); if (buf) buf += d; n += d; } break; case '?': fmt ++; switch (*fmt) { case '\0': if (buf) { bcopy("%?EOS",buf,5); buf += 5; } n += 5; goto out; /* break 2 */ break; default: if (buf) { *buf++ = '%'; *buf++ = '?'; *buf++ = '<'; *buf++ = '?'; *buf++ = *fmt; *buf++ = '>'; } n += 6; continue; break; case '-': cond = fmt_neg; break; case '+': cond = ! fmt_neg; break; } it = malloc(sizeof(ISTACK)); it->link = istack; istack = it; it->true = cond; break; case ':': if (istack) { istack->true = ! istack->true; } else { if (buf) { *buf++ = '%'; *buf++ = '?'; *buf++ = '<'; *buf++ = ':'; *buf++ = '>'; } n += 5; continue; } break; case '.': if (istack) { it = istack->link; istack = it; free(it); } else { if (buf) { *buf++ = '%'; *buf++ = '?'; *buf++ = '<'; *buf++ = '.'; *buf++ = '>'; } n += 5; continue; } break; } } out:; #undef INTRUEIF if (buf) *buf = '\0'; return(n); } static int fmt_amt_i(char *buf) { char tmp[64]; if (! buf) buf = &tmp[0]; sprintf(buf,"%lu",fmt_i); return(strlen(buf)); } static int fmt_amt_f(char *buf) { if (buf) sprintf(buf,"%0*lu",fmt_fplaces,fmt_f); return(fmt_fplaces); } int format_amount(char *buf, int neg, unsigned long int amt, const char *curr) { CURR *c; c = find_currency(curr); if (! c) { if (! unk_curr.name) init_unk_curr(); c = &unk_curr; } fmt_neg = neg; fmt_i = amt / c->divisor; fmt_fplaces = c->places; fmt_f = amt % c->divisor; return(do_format(buf,c->fmt,fmt_amt_i,fmt_amt_f)); } static int fmt_unk_i(char *buf) { if (buf) *buf++ = '?'; return(1); } static int fmt_unk_f(char *buf) { int i; if (buf) for (i=0;iplaces; return(do_format(buf,c->fmt,fmt_unk_i,fmt_unk_f)); }