bc: Use dynamic memory for strings - sbase - suckless unix tools
 (HTM) git clone git://git.suckless.org/sbase
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 2e9d76d21711d3c8f62d350e5cf9e68deda78031
 (DIR) parent 10ad90244eca21f033d59f808b45edf69060f5eb
 (HTM) Author: Roberto E. Vargas Caballero <k0ga@shike2.net>
       Date:   Tue, 25 Nov 2025 16:54:04 +0100
       
       bc: Use dynamic memory for strings
       
       Until now bc was using a clever mechanism that built all the strings
       in a single buffer that makes very easy handling them because no
       free was required. but it had a big problem because the size of the
       space used in this buffer grew exponentially because new strings
       were containing previous strings and all of them were living in memory
       at the same time. This new mechanism implements a printf alike
       and when strings are used as parameters it frees them, but it implies
       that we cannot use anymore constants as parameters and in some specific
       cases we have to free them manually.
       
       Diffstat:
         M bc.y                                |     129 ++++++++++++++++++++-----------
       
       1 file changed, 83 insertions(+), 46 deletions(-)
       ---
 (DIR) diff --git a/bc.y b/bc.y
       @@ -38,7 +38,7 @@ static char *code(char *, ...);
        static char *forcode(Macro *, char *, char *, char *, char *);
        static char *whilecode(Macro *, char *, char *);
        static char *ifcode(Macro *, char *, char *);
       -static char *funcode(Macro *, char *, char *, char *);
       +static void funcode(Macro *, char *, char *, char *);
        static Macro *define(char *, char *);
        static char *retcode(char *);
        static char *brkcode(void);
       @@ -132,9 +132,9 @@ stat    : exprstat
                | STRING                {$$ = code("[%s]P", $1);}
                | BREAK                 {$$ = brkcode();}
                | QUIT                  {quit();}
       -        | RETURN                {$$ = retcode("0");}
       +        | RETURN                {$$ = retcode(code("0"));}
                | RETURN '(' expr ')'   {$$ = retcode($3);}
       -        | RETURN '(' ')'        {$$ = retcode("0");}
       +        | RETURN '(' ')'        {$$ = retcode(code("0"));}
                | while cond stat       {$$ = whilecode($1, $2, $3);}
                | if cond stat          {$$ = ifcode($1, $2, $3);}
                | '{' statlst '}'       {$$ = $2;}
       @@ -153,25 +153,25 @@ for     : FOR                   {$$ = macro(LOOP, 0);}
        def     : DEF ID                {$$ = macro(DEF, funid($2));}
                ;
        
       -parlst  : '(' ')'               {$$ = "%s";}
       +parlst  : '(' ')'               {$$ = code("%%s");}
                | '(' params ')'        {$$ = $2;}
                ;
        
        params  : param
       -        | params ',' param      {$$ = code("%s%s", $1, $3);}
       +        | params ',' param      {$$ = code($1, $3); free($1);}
                ;
        
        param   : ID                    {$$ = code("S%s%%sL%ss.", var($1), var($1));}
                | ID '[' ']'            {$$ = code("S%s%%sL%ss.", ary($1), ary($1));}
                ;
        
       -autolst :                       {$$ = "%s";}
       +autolst :                       {$$ = code("%%s");}
                | AUTO locals '\n'      {$$ = $2;}
                | AUTO locals ';'       {$$ = $2;}
                ;
        
        locals  : local
       -        | locals ',' local      {$$ = code("%s%s", $1, $3);}
       +        | locals ',' local      {$$ = code($1, $3); free($1);}
                ;
        
        local   : ID                    {$$ = code("0S%s%%sL%ss.", var($1), var($1));}
       @@ -196,7 +196,7 @@ rel     : expr                  {$$ = code("%s 0!=", $1);}
                | expr '>' expr         {$$ = code("%s%s>", $3, $1);}
                ;
        
       -exprstat: nexpr                 {$$ = code("%s%ss.", $1, sflag ? "" : "p");}
       +exprstat: nexpr                 {$$ = code("%s%ss.", $1, code(sflag ? "" : "p"));}
                | assign                {$$ = code("%ss.", $1);}
                ;
        
       @@ -204,7 +204,7 @@ expr    : nexpr
                | assign
                ;
        
       -nexpr   : NUMBER                {$$ = code(" %s", $1);}
       +nexpr   : NUMBER                {$$ = code(" %s", code($1));}
                | ID                    {$$ = code("l%s", var($1));}
                | DOT                   {$$ = code("l.");}
                | SCALE                 {$$ = code("K");}
       @@ -224,16 +224,16 @@ nexpr   : NUMBER                {$$ = code(" %s", $1);}
                | LENGTH '(' expr ')'   {$$ = code("%sZ", $3);}
                | SQRT '(' expr ')'     {$$ = code("%sv", $3);}
                | SCALE '(' expr ')'    {$$ = code("%sX", $3);}
       -        | INCDEC ID             {$$ = code("l%s1%sds%s", var($2), $1, var($2));}
       -        | INCDEC SCALE          {$$ = code("K1%sk", $1);}
       -        | INCDEC IBASE          {$$ = code("I1%sdi", $1);}
       -        | INCDEC OBASE          {$$ = code("O1%sdo", $1);}
       -        | INCDEC ID ary         {$$ = code("%sdS_;%s1%sdL_:%s", $3, ary($2), $1, ary($2));}
       -        | ID INCDEC             {$$ = code("l%sd1%ss%s", var($1), $2, var($1));}
       -        | SCALE INCDEC          {$$ = code("Kd1%sk", $2);}
       -        | IBASE INCDEC          {$$ = code("Id1%si", $2);}
       -        | OBASE INCDEC          {$$ = code("Od1%so", $2);}
       -        | ID ary INCDEC         {$$ = code("%sds.;%sd1%sl.:%s", $2, ary($1), $3, ary($1));}
       +        | INCDEC ID             {$$ = code("l%s1%sds%s", var($2), code($1), var($2));}
       +        | INCDEC SCALE          {$$ = code("K1%sk", code($1));}
       +        | INCDEC IBASE          {$$ = code("I1%sdi", code($1));}
       +        | INCDEC OBASE          {$$ = code("O1%sdo", code($1));}
       +        | INCDEC ID ary         {$$ = code("%sdS_;%s1%sdL_:%s", $3, ary($2), code($1), ary($2));}
       +        | ID INCDEC             {$$ = code("l%sd1%ss%s", var($1), code($2), var($1));}
       +        | SCALE INCDEC          {$$ = code("Kd1%sk", code($2));}
       +        | IBASE INCDEC          {$$ = code("Id1%si", code($2));}
       +        | OBASE INCDEC          {$$ = code("Od1%so", code($2));}
       +        | ID ary INCDEC         {$$ = code("%sds.;%sd1%sl.:%s", $2, ary($1), code($3), ary($1));}
                ;
        
        assign  : ID '=' expr           {$$ = code("%sds%s", $3, var($1));}
       @@ -241,11 +241,11 @@ assign  : ID '=' expr           {$$ = code("%sds%s", $3, var($1));}
                | IBASE '=' expr        {$$ = code("%sdi", $3);}
                | OBASE '=' expr        {$$ = code("%sdo", $3);}
                | ID ary '=' expr       {$$ = code("%sd%s:%s", $4, $2, ary($1));}
       -        | ID EQOP expr          {$$ = code("%sl%s%sds%s", $3, var($1), $2, var($1));}
       -        | SCALE EQOP expr       {$$ = code("%sK%sdk", $3, $2);}
       -        | IBASE EQOP expr       {$$ = code("%sI%sdi", $3, $2);}
       -        | OBASE EQOP expr       {$$ = code("%sO%sdo", $3, $2);}
       -        | ID ary EQOP expr      {$$ = code("%s%sds.;%s%sdl.:s", $4, $2, ary($1), $3, ary($1));}
       +        | ID EQOP expr          {$$ = code("%sl%s%sds%s", $3, var($1), code($2), var($1));}
       +        | SCALE EQOP expr       {$$ = code("%sK%sdk", $3, code($2));}
       +        | IBASE EQOP expr       {$$ = code("%sI%sdi", $3, code($2));}
       +        | OBASE EQOP expr       {$$ = code("%sO%sdo", $3, code($2));}
       +        | ID ary EQOP expr      {$$ = code("%s%sds.;%s%sdl.:s", $4, $2, ary($1), code($3), ary($1));}
                ;
        
        ary     : '[' expr ']'          {$$ = $2;}
       @@ -267,6 +267,7 @@ writeout(char *s)
                        goto err;
                if (write(1, (char[]){'\n'}, 1) < 0)
                        goto err;
       +        free(s);
                return;
                
        err:
       @@ -276,22 +277,54 @@ err:
        static char *
        code(char *fmt, ...)
        {
       -        char *s;
       -        va_list va;
       -        size_t n, room;
       -
       -        va_start(va, fmt);
       -        room = BUFSIZ - used;
       -        n = vsnprintf(buff+used, room, fmt, va);
       -        va_end(va);
       +        char *s, *t;
       +        va_list ap;
       +        int c, len, room;
       +
       +        va_start(ap, fmt);
       +        room = BUFSIZ;
       +        for (s = buff; *fmt; s += len) {
       +                len = 1;
       +                if ((c = *fmt++) != '%')
       +                        goto append;
       +
       +                switch (*fmt++) {
       +                case 'd':
       +                        c = va_arg(ap, int);
       +                        len = snprintf(s, room, "%d", c);
       +                        if (len < 0 || len >= room)
       +                                goto err;
       +                        break;
       +                case 'c':
       +                        c = va_arg(ap, int);
       +                        goto append;
       +                case 's':
       +                        t = va_arg(ap, void *);
       +                        len = strlen(t);
       +                        if (len >= room)
       +                                goto err;
       +                        memcpy(s, t, len);
       +                        free(t);
       +                        break;
       +                case '%':
       +                append:
       +                        if (room <= 1)
       +                                goto err;
       +                        *s = c;
       +                        break;
       +                default:
       +                        abort();
       +                }
        
       -        if (n < 0 || n >= room)
       -                eprintf("unable to code requested operation\n");
       +                room -= len;
       +        }
       +        va_end(ap);
        
       -        s = buff + used;
       -        used += n + 1;
       +        *s = '\0';
       +        return estrdup(buff);
        
       -        return s;
       +err:
       +        eprintf("unable to code requested operation\n");
        }
        
        static Macro *
       @@ -311,18 +344,19 @@ macro(int op, int id)
                return d;
        }
        
       -static char *
       +static void
        funcode(Macro *d, char *params, char *vars, char *body)
        {
       -        char *s;
       +        char *s, *t1, *t2;
        
       -        s = code(vars, params);
       -        s = code(s, body);
       -        s = code("[%s 0 1Q]s%c", s, d->id);
       +        t1 = code(vars, params);
       +        t2 = code(t1, body);
       +        s = code("[%s 0 1Q]s%c", t2, d->id);
                nested--;
                writeout(s);
        
       -        return s;
       +        free(vars);
       +        free(t1);
        }
        
        static char *
       @@ -345,7 +379,7 @@ forcode(Macro *d, char *init, char *cmp, char *inc, char *body)
                s = code("[%s%ss.%s%c]s%c",
                         body,
                         inc,
       -                 cmp,
       +                 estrdup(cmp),
                         d->flowid, d->flowid);
                writeout(s);
        
       @@ -360,7 +394,10 @@ whilecode(Macro *d, char *cmp, char *body)
        {
                char *s;
        
       -        s = code("[%ss.%s%c]s%c", body, cmp, d->flowid, d->flowid);
       +        s = code("[%ss.%s%c]s%c",
       +                 body,
       +                 estrdup(cmp),
       +                 d->flowid, d->flowid);
                writeout(s);
        
                s = code("%s%c", cmp, d->flowid);
       @@ -408,7 +445,7 @@ ftn(char *s)
        static char *
        var(char *s)
        {
       -        return code("%s", s);
       +        return code(s);
        }
        
        static void