On 2/27/24 13:46, Oliver Webb via Toybox wrote:
> Since we build toybox with -funsigned-char, there is no reason to have a type 
> for unnsigned chars in bc.c,
> since that is the default for all chars. And removing it gets rid of a typedef

Sigh, the bc in the tree is "fraught". The guy who submitted it said he was
escaping from a cloud of drama at the time (I didn't care), and then was
surrounded by a fresh cloud of drama after "escaping" and tried to suck me into
it (I declined). He wandered off to busybox and also maintaining his command as
a separate project.

According to "wc toys/*/*.c | sort -n" it is the largest command in the entire
toybox tree. Larger than the shell despite not having a dozen subcommands nor
needing to manage terminal control or child processes. It's 5 times the size or
sed or tar, and at least 3 times the size it NEEDS to be. I'd far rather clean
up xzcat or implement awk (each of which is a similar effort ballpark) than
spend months cleaning it up.

Modern users who want to do funky math pull out python or something. Modern
users who want to do non-funky math have $((math)) built into the shell, and
expr, and are as likely to use dc as bc. A simple tool that does floating point
math would be nice, and I got a submission of a tiny floating point calculator
(attached) a couple years back which I haven't checked in yet because it's not a
standard command and I don't know anybody under 50 who uses reverse polish
notation, but I have a todo item to maybe have toysh's $[] syntax (a deprecated
synonym for $(()) from before posix standardized that) do floating point math?
(I mean, since it's deprecated...) Alas there's some design work: rpn.c uses
letters for sqrt() sin() asin() cos() acos() tan() atan() and has a PI constant,
where integer $((math)) in the shell expands any leftover strings it sees to
shell environment variables. Still, pulling out function() calls before
recognizing variable names isn't that big a lift... But let's get the base shell
features all working first, then worry about extensions.

But the FACT that people tend to do complex math in perl or $HOSTCC or similar
means the only confirmed user of bc I've found is the linux kernel build, and
I've had a patch to remove "bc" from that for ten years. (Which still worked
last I checked, which was... the 6.7 release build I think?) I want to build
linux from scratch, and then try to populate a debian repository under an LFS
system (hard problem), and THEN decide whether to clean this command up or
simply remove it. Maintaining "not bc" package builds is a heck of a lot less
work than maintaining "builds against musl" package builds.

There's no strong reason _not_ to apply your patch, just...

Rob
/* rpn.c - Reverse Polish Notation floating point calculator
 *
 * Copyright 2022 Jeff Dionne <[email protected]>
 *
 * No standard. (Possibly some IEEE thing?)

USE_RPN(NEWTOY(rpn, "<2", TOYFLAG_USR|TOYFLAG_BIN))

config RPN
  bool "rpn"
  default n
  help
    usage: rpn 

    A hello world program.

    Mostly used as a simple template for adding new commands.
    Occasionally nice to smoketest kernel booting via "init=/usr/bin/hello".
*/

#define FOR_rpn
#include "toys.h"

GLOBALS(
  int unused;
)

#define STACK_LEN 4

typedef struct _val {
   int s;
   unsigned int i;
   unsigned int f;
   unsigned int p;
   int e;
   int flags;
} fp_comp_t;
static struct _val zero_fp = { 1, 0, 0, 0, 0, 0 };

static double stack[STACK_LEN];
static int sp = STACK_LEN;

fp_comp_t decomp(double v, int eng)
{
   fp_comp_t d;
   unsigned int t;
   unsigned int m=100000;

   d.s = v>=0 ? 1 : -1;
     v = v> 0 ? v : -v;

   /* extract 6 significant digits */
   d.e = d.p = 5;
   while (v < m   && v) { v *= 10; d.e--; }
   while (v>= 10*m    ) { v /= 10; d.e++; }

   /* engineering mode exp adjust n, u, m, k, M... */
   if (eng) while (d.e%3) { m /= 10; d.e--; d.p--; }

   t   = v+0.5;
   d.i  = t/m;
   d.f = t - (d.i)*m;

   return d;
}

double comp(fp_comp_t v)
{
   double ret;

   ret = v.f;
   while (v.p--) { ret /= 10; }
   ret += v.i;
   while (v.e > 0) { ret *= 10; v.e--; }
   while (v.e < 0) { ret /= 10; v.e++; }
   if (v.s < 0) return -ret;
   return ret;
}

void print_decomp(fp_comp_t d, int f, int e)
{
    if (e)      printf("%c%d.%.*de%+d\n", d.s>0 ? ' ':'-', d.i, d.p, d.f, d.e);
    else if (f) printf("%c%d.%.*d\n",     d.s>0 ? ' ':'-', d.i, d.p, d.f     );
    else        printf("%c%d\n",          d.s>0 ? ' ':'-', d.i               );
}

fp_comp_t key_edit(fp_comp_t d, char in)
{
   switch (d.flags) {
   case 0:
   case 10:
      d = zero_fp;
      d.flags++;
      if (in == 'e') d.i=1;
   case 1:
      if (isdigit(in)) { d.i = d.i*10 + in-'0'; }
      if (in == 'n') d.s = -d.s;
      if (in == '.') d.flags++;
      if (in == 'e') d.flags = 3;
      if (in == 'b') d.i /= 10;
      break;
   case 2:
      if (isdigit(in)) { d.f = d.f*10 + in-'0'; d.p++; }
      if (in == 'n') d.s = -d.s;
      if (in == 'e') d.flags++;
      if (in == 'b') {
         d.f /= 10;
         if (!d.p) d.flags--;
         else d.p--;
      }
      break;
   case 3:
   case 7:
      if (isdigit(in)) { d.e = d.e*10 + in-'0'; }
      if (in == 'n') {
         if (!d.e) d.flags ^= 4;
         d.e = -d.e;
      }
      if (d.e && d.flags & 4) { d.e = -d.e; d.flags &= 4; }
      if (in == 'b') {
         if (!d.e) d.flags = 2;
         d.e /= 10;
      }
      break;
   }
   return d;
}

void push(double v)
{
   if (sp) stack[--sp] = v;
}

double pop()
{
   return (sp<STACK_LEN) ? stack[sp++] : 0;
}

fp_comp_t key_process(fp_comp_t d, char in)
{
   double x, y;

   if (strchr("+-*/wvqQsScCtT d", in)) {
      if (d.flags && d.flags != 10) push(comp(d));
      x = pop();
   }
   if (strchr("+-*/w"           , in)) y = pop();
   if (strchr("+-*/wvqQsScCtTD" , in)) {
      d.flags = 0;
      switch (in) {
      case '+': x = y + x; break;
      case '-': x = y - x; break;
      case '*': x = y * x; break;
      case '/': x = y     / (x ? x : 1e-300); break; // avoid div by zero
      case 'w': push(x); x = y; break;
      case 'v': x = (1.0) / (x ? x : 1e-300); break; // avoid div by zero
      case 'q': x = sqrt(x); break;
      case 'Q': x = x*x; break;
      case 's': x =  sin(x); break;
      case 'S': x = asin(x); break;
      case 'c': x =  cos(x); break;
      case 'C': x = acos(x); break;
      case 't': x =  tan(x); break;
      case 'T': x = atan(x); break;
      case 'D': x = M_PI; break;
      }
      push(x); 
   } else if (in=='z') {
       if (!d.flags) d=decomp(stack[sp], 1);
       push(comp(d));
       d.flags = 10;
   } else {
       d = key_edit(d, in);
   }
#ifdef DEBUG
   if (!d.flags ) print_decomp(decomp(stack[sp], 1), 1, 1);
   if ( d.flags ) print_decomp(d, d.flags == 2, d.flags == 3 || d.flags == 7);
#endif
   return d;
}

int main(int argc, char *argv[])
{
   fp_comp_t d = zero_fp;
   char *p;
   int i;
   int a = 1;

   if (argc < 2) exit(0);

   p = argv[a++];
   while (*p) {
      d = key_process(d, *p);
      if (!*++p && a<argc) p = argv[a++];
   }
   for (i=STACK_LEN; i>sp; ) print_decomp(decomp(stack[--i], 1), 1, 1);

   return 0;
}
_______________________________________________
Toybox mailing list
[email protected]
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to