tinymux/mux/modules/engine/unparse.cpp
Stephen Dennis 974964719c Convert all static scratch buffers to thread_local
43 static scratch-buffer arrays across 21 files (5 in mux/src, 38 in
mux/modules/) changed from `static` to `thread_local`. Under the
current single-threaded evaluator this is a zero-behavior-change swap
— `thread_local` storage has the same lifetime and zero-allocation
properties as `static` — but each thread gets its own copy, which
makes these functions safe for a future multi-threaded evaluator
without any locking.

Read-only constant tables (`aRadix64`, `aRadixPenn36`,
`aRadixPenn64`, `Empty`) left as `static` because they are immutable
shared data.

Affected areas:
  net.cpp        — queue_string co_buf, trimmed_site, dump_users NameField
  signals.cpp    — signal_desc
  stubslave.cpp  — Stub_PipePump
  attrcache.cpp  — sqlite_attr_buf
  boolexp.cpp    — parsestore
  command.cpp    — preserve_cmd, SpaceCompressCommand, LowerCaseCommand
  comsys.cpp     — NewTitle, Buffer, temp
  db.cpp         — tbuff, Buffer (x2)
  flags.cpp      — buff
  funceval.cpp   — textbuff
  functions.cpp  — TimeBuffer64, TimeBuffer80, Buffer
  help.cpp       — Line, Buffer
  mail.cpp       — aFolders, Buffer, res, szFittedMailAliasDesc
  match.cpp      — buffer
  player.cpp     — szSalt, buf (x2), buff
  plusemail.cpp   — buf
  predicates.cpp — Buf (x2), pName
  session.cpp    — szFittedDoing
  set.cpp        — pRestrictedKeyText
  unparse.cpp    — buf, boolexp_buf
  mail_mod.cpp   — result, res, buf

All 21 files verified with g++ -std=c++17 -fsyntax-only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:21:14 -06:00

257 lines
7 KiB
C++

/*! \file unparse.cpp
* \brief Produces human-readable key info from boolexps.
*
*/
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
// Boolexp decompile formats
//
#define F_EXAMINE 1 // Normal
#define F_QUIET 2 // Binary for db dumps
#define F_DECOMPILE 3 // @decompile output
#define F_FUNCTION 4 // [lock()] output
/*
* Take a dbref (loc) and generate a string. -1, -3, or (#loc) Note, this
* will give players object numbers of stuff they don't control, but it's
* only internal currently, so it's not a problem.
*/
static UTF8 *unparse_object_quiet(dbref player, dbref loc)
{
UNUSED_PARAMETER(player);
thread_local UTF8 buf[SBUF_SIZE];
mux_sprintf(buf, SBUF_SIZE, T("(#%d)"), loc);
return buf;
}
thread_local UTF8 boolexp_buf[LBUF_SIZE];
static UTF8 *buftop;
static void unparse_boolexp1(dbref player, BOOLEXP *b, UTF8 outer_type, int format)
{
if (TRUE_BOOLEXP == b)
{
if (format == F_EXAMINE)
{
safe_str(T("*UNLOCKED*"), boolexp_buf, &buftop);
}
return;
}
switch (b->type)
{
case BOOLEXP_AND:
if (outer_type == BOOLEXP_NOT)
{
safe_chr('(', boolexp_buf, &buftop);
}
unparse_boolexp1(player, b->sub1, b->type, format);
safe_chr(AND_TOKEN, boolexp_buf, &buftop);
unparse_boolexp1(player, b->sub2, b->type, format);
if (outer_type == BOOLEXP_NOT)
{
safe_chr(')', boolexp_buf, &buftop);
}
break;
case BOOLEXP_OR:
if (outer_type == BOOLEXP_NOT || outer_type == BOOLEXP_AND)
{
safe_chr('(', boolexp_buf, &buftop);
}
unparse_boolexp1(player, b->sub1, b->type, format);
safe_chr(OR_TOKEN, boolexp_buf, &buftop);
unparse_boolexp1(player, b->sub2, b->type, format);
if (outer_type == BOOLEXP_NOT || outer_type == BOOLEXP_AND)
{
safe_chr(')', boolexp_buf, &buftop);
}
break;
case BOOLEXP_NOT:
safe_chr('!', boolexp_buf, &buftop);
unparse_boolexp1(player, b->sub1, b->type, format);
break;
case BOOLEXP_INDIR:
safe_chr(INDIR_TOKEN, boolexp_buf, &buftop);
unparse_boolexp1(player, b->sub1, b->type, format);
if (b->thing != A_LOCK)
{
// Reverse-lookup lock name from lock_sw[].
//
for (NAMETAB *nt = lock_sw; nt->name; nt++)
{
if (nt->flag == static_cast<unsigned int>(b->thing))
{
safe_chr('/', boolexp_buf, &buftop);
safe_str(nt->name, boolexp_buf, &buftop);
break;
}
}
}
break;
case BOOLEXP_IS:
safe_chr(IS_TOKEN, boolexp_buf, &buftop);
unparse_boolexp1(player, b->sub1, b->type, format);
break;
case BOOLEXP_CARRY:
safe_chr(CARRY_TOKEN, boolexp_buf, &buftop);
unparse_boolexp1(player, b->sub1, b->type, format);
break;
case BOOLEXP_OWNER:
safe_chr(OWNER_TOKEN, boolexp_buf, &buftop);
unparse_boolexp1(player, b->sub1, b->type, format);
break;
case BOOLEXP_CONST:
if (mudstate.bStandAlone)
{
safe_str(unparse_object_quiet(player, b->thing),
boolexp_buf, &buftop);
}
else
{
switch (format)
{
case F_QUIET:
// Quiet output - for dumps and internal use. Always #Num.
//
safe_str(unparse_object_quiet(player, b->thing),
boolexp_buf, &buftop);
break;
case F_EXAMINE:
// Examine output - informative. Name(#Num) or Name.
//
UTF8 *buff;
buff = unparse_object(player, b->thing, false);
safe_str(buff, boolexp_buf, &buftop);
free_lbuf(buff);
break;
case F_DECOMPILE:
// Decompile output - should be usable on other MUXes. Name if
// player, Name if thing, else #Num.
//
switch (Typeof(b->thing))
{
case TYPE_PLAYER:
safe_chr('*', boolexp_buf, &buftop);
case TYPE_THING:
safe_str(Name(b->thing), boolexp_buf, &buftop);
break;
default:
safe_tprintf_str(boolexp_buf, &buftop, T("#%d"), b->thing);
break;
}
break;
case F_FUNCTION:
// Function output - must be usable by @lock cmd. Name if player,
// else #Num.
//
switch (Typeof(b->thing))
{
case TYPE_PLAYER:
safe_chr('*', boolexp_buf, &buftop);
safe_str(Name(b->thing), boolexp_buf, &buftop);
break;
default:
safe_tprintf_str(boolexp_buf, &buftop, T("#%d"), b->thing);
break;
}
}
}
break;
case BOOLEXP_ATR:
case BOOLEXP_EVAL:
ATTR *ap;
ap = atr_num(b->thing);
if (ap && ap->number)
{
// Use the attribute name if the attribute exists.
//
safe_str(ap->name, boolexp_buf, &buftop);
}
else
{
// Otherwise use the attribute number.
//
// Only god or the db loader can create a new boolexp
// with an invalid attribute, but anyone may keep a lock
// whose attribute has subsequently disappeared.
//
safe_ltoa(b->thing, boolexp_buf, &buftop);
}
if (b->type == BOOLEXP_EVAL)
{
safe_chr('/', boolexp_buf, &buftop);
}
else
{
safe_chr(':', boolexp_buf, &buftop);
}
safe_str(reinterpret_cast<UTF8 *>(b->sub1), boolexp_buf, &buftop);
break;
default:
// Bad type.
//
Log.WriteString(T("ABORT! unparse.cpp, fell off the end of switch in unparse_boolexp1()" ENDLINE));
Log.Flush();
abort();
break;
}
}
UTF8 *unparse_boolexp_quiet(dbref player, BOOLEXP *b)
{
buftop = boolexp_buf;
unparse_boolexp1(player, b, BOOLEXP_CONST, F_QUIET);
*buftop = '\0';
return boolexp_buf;
}
UTF8 *unparse_boolexp(dbref player, BOOLEXP *b)
{
buftop = boolexp_buf;
unparse_boolexp1(player, b, BOOLEXP_CONST, F_EXAMINE);
*buftop = '\0';
return boolexp_buf;
}
UTF8 *unparse_boolexp_decompile(dbref player, BOOLEXP *b)
{
buftop = boolexp_buf;
unparse_boolexp1(player, b, BOOLEXP_CONST, F_DECOMPILE);
*buftop = '\0';
return boolexp_buf;
}
UTF8 *unparse_boolexp_function(dbref player, BOOLEXP *b)
{
buftop = boolexp_buf;
unparse_boolexp1(player, b, BOOLEXP_CONST, F_FUNCTION);
*buftop = '\0';
return boolexp_buf;
}