Files
calc/symbol.c
Landon Curt Noll d89ea78104 prep for calc version 2.14.1.4
Updated COPYING file to indicate that these files are now
covered under "The Unlicense" (see https://unlicense.org):

	sha1.c
	sha1.h
	cal/dotest.cal
	cal/screen.cal

Updated help/credit to match the above changes to COPYING.

Updated CONTRIB-CODE and calc.man to refer to using GitHub pull requests
for contributing to calc (instead of using Email).

Updated BUGS and calc.man to refer to using GitHub issues
for reporting calc bugs (instead of using Email).

Updated QUESTIONS and calc.man to refer to using GitHub issues
for asking calc questions (instead of using Email).

Fixed Makefile.local command example to refer to overriding the
Makefile variable DEBUG (instead of CDEBUG).

Fixed all make chk ASAN warnings under macOS 13.2.1 when calc is compiled
with the following uncommented lines in Makefile.local:

    DEBUG:= -O0 -g
    CFLAGS+= -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
    LDFLAGS+= -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
    CALC_ENV+= ASAN_OPTIONS=detect_stack_use_after_return=1

Improved how pointers to functions are declared for the builtins
array in func.c to avoid function declarations without a prototype
that is now deprecated in C.

Improved how pointers to functions are declared for the opcodes
array in opcodes.c to avoid function declarations without a prototype

Replaced use of egrep with grep -E in Makefiles.

Fixed cases where variables were set but not used in symbol.c, calc.c,
and the main function in func.c as used by funclist.c.

Added rule name to "DO NOT EDIT -- generated by the Makefile" lines
in constructed files.

Test if <sys/vfs.h> exists and set HAVE_SYS_VFS_H accordingly
in have_sys_vfs.h.  Added HAVE_SYS_VFS_H to allow one to force
this value.

Test if <sys/param.h> exists and set HAVE_SYS_PARAM_H accordingly
in have_sys_param.h.  Added HAVE_SYS_PARAM_H to allow one to force
this value.

Test if <sys/mount.h> exists and set HAVE_SYS_MOUNT_H accordingly
in have_sys_mount.h.  Added HAVE_SYS_MOUNT_H to allow one to force
this value.

Test if the system as the statfs() system call and set HAVE_STATFS
accordingly in have_statfs.h.  Added HAVE_STATFS to allow one
to force this value.

The pseudo_seed() function will also try to call statfs() if
possible and permitted by the HAVE_STATFS value.
2023-03-06 02:17:40 -08:00

774 lines
16 KiB
C

/*
* symbol - global and local symbol routines
*
* Copyright (C) 1999-2007,2021,2022 David I. Bell and Ernest Bowen
*
* Primary author: David I. Bell
*
* Calc is open software; you can redistribute it and/or modify it under
* the terms of the version 2.1 of the GNU Lesser General Public License
* as published by the Free Software Foundation.
*
* Calc is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
* Public License for more details.
*
* A copy of version 2.1 of the GNU Lesser General Public License is
* distributed with calc under the filename COPYING-LGPL. You should have
* received a copy with calc; if not, write to Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Under source code control: 1990/02/15 01:48:23
* File existed as early as: before 1990
*
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
*/
#include <stdio.h>
#include "calc.h"
#include "token.h"
#include "symbol.h"
#include "str.h"
#include "opcodes.h"
#include "func.h"
#include "attribute.h"
#include "banned.h" /* include after system header <> includes */
#define HASHSIZE 37 /* size of hash table */
E_FUNC FILE *f_open(char *name, char *mode);
STATIC int filescope; /* file scope level for static variables */
STATIC int funcscope; /* function scope level for static variables */
STATIC STRINGHEAD localnames; /* list of local variable names */
STATIC STRINGHEAD globalnames; /* list of global variable names */
STATIC STRINGHEAD paramnames; /* list of parameter variable names */
STATIC GLOBAL *globalhash[HASHSIZE]; /* hash table for globals */
S_FUNC void printtype(VALUE *);
S_FUNC void unscope(void);
S_FUNC void addstatic(GLOBAL *);
STATIC long staticcount = 0;
STATIC long staticavail = 0;
STATIC GLOBAL **statictable;
/*
* Hash a symbol name so we can find it in the hash table.
* Args are the symbol name and the symbol name size.
*/
#define HASHSYM(n, s) ((unsigned)((n)[0]*123 + (n)[s-1]*135 + (s)*157) % \
HASHSIZE)
/*
* Initialize the global symbol table.
*/
void
initglobals(void)
{
int i; /* index counter */
for (i = 0; i < HASHSIZE; i++)
globalhash[i] = NULL;
initstr(&globalnames);
filescope = SCOPE_STATIC;
funcscope = 0;
}
/*
* Define a possibly new global variable which may or may not be static.
* If it did not already exist, it is created with a value of zero.
* The address of the global symbol structure is returned.
*
* given:
* name name of global variable
* isstatic TRUE if symbol is static
*/
GLOBAL *
addglobal(char *name, BOOL isstatic)
{
GLOBAL *sp; /* current symbol pointer */
GLOBAL **hp; /* hash table head address */
size_t len; /* length of string */
int newfilescope; /* file scope being looked for */
int newfuncscope; /* function scope being looked for */
newfilescope = SCOPE_GLOBAL;
newfuncscope = 0;
if (isstatic) {
newfilescope = filescope;
newfuncscope = funcscope;
}
len = strlen(name);
if (len <= 0)
return NULL;
hp = &globalhash[HASHSYM(name, len)];
for (sp = *hp; sp; sp = sp->g_next) {
if ((sp->g_len == len) &&
(strncmp(sp->g_name, name, len+1) == 0)
&& (sp->g_filescope == newfilescope)
&& (sp->g_funcscope == newfuncscope))
return sp;
}
sp = (GLOBAL *) malloc(sizeof(GLOBAL));
if (sp == NULL)
return sp;
sp->g_name = addstr(&globalnames, name);
sp->g_len = len;
sp->g_filescope = newfilescope;
sp->g_funcscope = newfuncscope;
sp->g_value.v_num = qlink(&_qzero_);
sp->g_value.v_type = V_NUM;
sp->g_value.v_subtype = V_NOSUBTYPE;
sp->g_next = *hp;
*hp = sp;
return sp;
}
/*
* Look for the highest-scope global variable with a specified name.
* Returns the address of the variable or NULL according as the search
* succeeds or fails.
*/
GLOBAL *
findglobal(char *name)
{
GLOBAL *sp; /* current symbol pointer */
GLOBAL *bestsp; /* found symbol with highest scope */
size_t len; /* length of string */
bestsp = NULL;
len = strlen(name);
for (sp = globalhash[HASHSYM(name, len)]; sp != NULL; sp = sp->g_next) {
if ((sp->g_len == len) &&
(strncmp(sp->g_name, name, len+1) == 0)) {
if ((bestsp == NULL) ||
(sp->g_filescope > bestsp->g_filescope) ||
(sp->g_funcscope > bestsp->g_funcscope))
bestsp = sp;
}
}
return bestsp;
}
/*
* Return the name of a global variable given its address.
*
* given:
* sp address of global pointer
*/
char *
globalname(GLOBAL *sp)
{
if (sp)
return sp->g_name;
return "";
}
/*
* Show the value of all real-number valued global variables, displaying
* only the head and tail of very large numerators and denominators.
* Static variables are not included.
*/
void
showglobals(void)
{
GLOBAL **hp; /* hash table head address */
register GLOBAL *sp; /* current global symbol pointer */
long count; /* number of global variables shown */
count = 0;
for (hp = &globalhash[HASHSIZE-1]; hp >= globalhash; hp--) {
for (sp = *hp; sp; sp = sp->g_next) {
if (sp->g_value.v_type != V_NUM)
continue;
if (count++ == 0) {
printf("\nName Digits Value\n");
printf( "---- ------ -----\n");
}
printf("%-8s", sp->g_name);
if (sp->g_filescope != SCOPE_GLOBAL)
printf(" (s)");
fitprint(sp->g_value.v_num, 50);
printf("\n");
}
}
if (count == 0) {
printf("No real-valued global variables\n");
}
putchar('\n');
}
void
showallglobals(void)
{
GLOBAL **hp; /* hash table head address */
register GLOBAL *sp; /* current global symbol pointer */
long count; /* number of global variables shown */
count = 0;
for (hp = &globalhash[HASHSIZE-1]; hp >= globalhash; hp--) {
for (sp = *hp; sp; sp = sp->g_next) {
if (count++ == 0) {
printf("\nName Level Type\n");
printf( "---- ----- -----\n");
}
printf("%-8s%4d ", sp->g_name, sp->g_filescope);
printtype(&sp->g_value);
printf("\n");
}
}
if (count > 0)
printf("\nNumber: %ld\n", count);
else
printf("No global variables\n");
}
S_FUNC void
printtype(VALUE *vp)
{
int type;
char *s;
type = vp->v_type;
if (type < 0) {
printf("Error %d", -type);
return;
}
switch (type) {
case V_NUM:
printf("real = ");
fitprint(vp->v_num, 32);
return;
case V_COM:
printf("complex = ");
fitprint(vp->v_com->real, 8);
if (!vp->v_com->imag->num.sign)
printf("+");
fitprint(vp->v_com->imag, 8);
printf("i");
return;
case V_STR:
printf("string = \"");
fitstring(vp->v_str->s_str, vp->v_str->s_len, 50);
printf("\"");
return;
case V_NULL:
s = "null";
break;
case V_MAT:
s = "matrix";
break;
case V_LIST:
s = "list";
break;
case V_ASSOC:
s = "association";
break;
case V_OBJ:
printf("%s ", objtypename(
vp->v_obj->o_actions->oa_index));
s = "object";
break;
case V_FILE:
s = "file id";
break;
case V_RAND:
s = "subtractive 100 random state";
break;
case V_RANDOM:
s = "Blum random state";
break;
case V_CONFIG:
s = "config state";
break;
case V_HASH:
s = "hash state";
break;
case V_BLOCK:
s = "unnamed block";
break;
case V_NBLOCK:
s = "named block";
break;
case V_VPTR:
s = "value pointer";
break;
case V_OPTR:
s = "octet pointer";
break;
case V_SPTR:
s = "string pointer";
break;
case V_NPTR:
s = "number pointer";
break;
default:
s = "???";
break;
}
printf("%s", s);
}
/*
* Write all normal global variables to an output file.
* Note: Currently only simple types are saved.
* Returns nonzero on error.
*/
int
writeglobals(char *name)
{
FILE *fp;
GLOBAL **hp; /* hash table head address */
register GLOBAL *sp; /* current global symbol pointer */
int savemode; /* saved output mode */
E_FUNC void math_setfp(FILE *fp);
fp = f_open(name, "w");
if (fp == NULL)
return 1;
math_setfp(fp);
for (hp = &globalhash[HASHSIZE-1]; hp >= globalhash; hp--) {
for (sp = *hp; sp; sp = sp->g_next) {
switch (sp->g_value.v_type) {
case V_NUM:
case V_COM:
case V_STR:
break;
default:
continue;
}
math_fmt("%s = ", sp->g_name);
savemode = math_setmode(MODE_HEX);
printvalue(&sp->g_value, PRINT_UNAMBIG);
math_setmode(savemode);
math_str(";\n");
}
}
math_setfp(stdout);
if (fclose(fp))
return 1;
return 0;
}
/*
* Free all non-null global and visible static variables
*/
void
freeglobals(void)
{
GLOBAL **hp; /* hash table head address */
GLOBAL *sp; /* current global symbol pointer */
/*
* We prevent the hp pointer from walking behind globalhash
* by stopping one short of the end and running the loop one
* more time.
*
* We could stop the loop with just hp >= globalhash, but stopping
* short and running the loop one last time manually helps make
* code checkers such as insure happy.
*/
for (hp = &globalhash[HASHSIZE-1]; hp > globalhash; hp--) {
for (sp = *hp; sp; sp = sp->g_next) {
if (sp->g_value.v_type != V_NULL) {
freevalue(&sp->g_value);
}
}
}
/* run the loop manually one last time */
for (sp = *hp; sp; sp = sp->g_next) {
if (sp->g_value.v_type != V_NULL) {
freevalue(&sp->g_value);
}
}
}
/*
* Free all invisible static variables
*/
void
freestatics(void)
{
GLOBAL **stp;
GLOBAL *sp;
long count;
stp = statictable;
count = staticcount;
while (count-- > 0) {
sp = *stp++;
freevalue(&sp->g_value);
}
}
/*
* Reset the file and function scope levels back to the original values.
* This is called on errors to forget any static variables which were being
* defined.
*/
void
resetscopes(void)
{
filescope = SCOPE_STATIC;
funcscope = 0;
unscope();
}
/*
* Enter a new file scope level so that newly defined static variables
* will have the appropriate scope, and so that previously defined static
* variables will temporarily be unaccessible. This should only be called
* when the function scope level is zero.
*/
void
enterfilescope(void)
{
filescope++;
funcscope = 0;
}
/*
* Exit from a file scope level. This deletes from the global symbol table
* all of the static variables that were defined within this file scope level.
* The function scope level is also reset to zero.
*/
void
exitfilescope(void)
{
if (filescope > SCOPE_STATIC)
filescope--;
funcscope = 0;
unscope();
}
/*
* Enter a new function scope level within the current file scope level.
* This allows newly defined static variables to override previously defined
* static variables in the same file scope level.
*/
void
enterfuncscope(void)
{
funcscope++;
}
/*
* Exit from a function scope level. This deletes static symbols which were
* defined within the current function scope level, and makes previously
* defined symbols with the same name within the same file scope level
* accessible again.
*/
void
exitfuncscope(void)
{
if (funcscope > 0)
funcscope--;
unscope();
}
/*
* To end the scope of any static variable with identifier id when
* id is being declared as global, or when id is declared as static and the
* variable is at the same file and function level.
*/
void
endscope(char *name, BOOL isglobal)
{
GLOBAL *sp;
GLOBAL *prevsp;
GLOBAL **hp;
size_t len;
len = strlen(name);
prevsp = NULL;
hp = &globalhash[HASHSYM(name, len)];
for (sp = *hp; sp; sp = sp->g_next) {
if (sp->g_len == len && !strcmp(sp->g_name, name) &&
sp->g_filescope > SCOPE_GLOBAL) {
if (isglobal || (sp->g_filescope == filescope &&
sp->g_funcscope == funcscope)) {
addstatic(sp);
if (prevsp)
prevsp->g_next = sp->g_next;
else
*hp = sp->g_next;
continue;
}
}
prevsp = sp;
}
}
/*
* To store in a table a static variable whose scope is being ended
*/
void
addstatic(GLOBAL *sp)
{
GLOBAL **stp;
if (staticavail <= 0) {
if (staticcount <= 0)
stp = (GLOBAL **) malloc(20 * sizeof(GLOBAL *));
else
stp = (GLOBAL **) realloc(statictable,
(20 + staticcount) * sizeof(GLOBAL *));
if (stp == NULL) {
math_error("Cannot allocate static-variable table");
not_reached();
}
statictable = stp;
staticavail = 20;
}
statictable[staticcount++] = sp;
staticavail--;
}
/*
* To display all static variables whose scope has been ended
*/
void
showstatics(void)
{
long count;
GLOBAL **stp;
GLOBAL *sp;
for (count = 0, stp = statictable; count < staticcount; count++) {
sp = *stp++;
if (count == 0) {
printf("\nName Scopes Type\n");
printf( "---- ------ -----\n");
}
printf("%-8s", sp->g_name);
printf("%3d", sp->g_filescope);
printf("%3d ", sp->g_funcscope);
printtype(&sp->g_value);
printf("\n");
}
if (count > 0)
printf("\nNumber: %ld\n", count);
else
printf("No un-scoped static variables\n");
}
/*
* Remove all the symbols from the global symbol table which have file or
* function scopes larger than the current scope levels. Their memory
* remains allocated since their values still actually exist.
*/
S_FUNC void
unscope(void)
{
GLOBAL **hp; /* hash table head address */
register GLOBAL *sp; /* current global symbol pointer */
GLOBAL *prevsp; /* previous kept symbol pointer */
/*
* We prevent the hp pointer from walking behind globalhash
* by stopping one short of the end and running the loop one
* more time.
*
* We could stop the loop with just hp >= globalhash, but stopping
* short and running the loop one last time manually helps make
* code checkers such as insure happy.
*/
for (hp = &globalhash[HASHSIZE-1]; hp > globalhash; hp--) {
prevsp = NULL;
for (sp = *hp; sp; sp = sp->g_next) {
if ((sp->g_filescope == SCOPE_GLOBAL) ||
(sp->g_filescope < filescope) ||
((sp->g_filescope == filescope) &&
(sp->g_funcscope <= funcscope))) {
prevsp = sp;
continue;
}
/*
* This symbol needs removing.
*/
addstatic(sp);
if (prevsp)
prevsp->g_next = sp->g_next;
else
*hp = sp->g_next;
}
}
/* run the loop manually one last time */
prevsp = NULL;
for (sp = *hp; sp; sp = sp->g_next) {
if ((sp->g_filescope == SCOPE_GLOBAL) ||
(sp->g_filescope < filescope) ||
((sp->g_filescope == filescope) &&
(sp->g_funcscope <= funcscope))) {
prevsp = sp;
continue;
}
/*
* This symbol needs removing.
*/
addstatic(sp);
if (prevsp)
prevsp->g_next = sp->g_next;
else
*hp = sp->g_next;
}
}
/*
* Initialize the local and parameter symbol table information.
*/
void
initlocals(void)
{
initstr(&localnames);
initstr(&paramnames);
curfunc->f_localcount = 0;
curfunc->f_paramcount = 0;
}
/*
* Add a possibly new local variable definition.
* Returns the index of the variable into the local symbol table.
* Minus one indicates the symbol could not be added.
*
* given:
* name name of local variable
*/
long
addlocal(char *name)
{
long index; /* current symbol index */
index = findstr(&localnames, name);
if (index >= 0)
return index;
index = localnames.h_count;
(void) addstr(&localnames, name);
curfunc->f_localcount++;
return index;
}
/*
* Find a local variable name and return its index.
* Returns minus one if the variable name is not defined.
*
* given:
* name name of local variable
*/
long
findlocal(char *name)
{
return findstr(&localnames, name);
}
/*
* Return the name of a local variable.
*/
char *
localname(long n)
{
return namestr(&localnames, n);
}
/*
* Add a possibly new parameter variable definition.
* Returns the index of the variable into the parameter symbol table.
* Minus one indicates the symbol could not be added.
*
* given:
* name name of parameter variable
*/
long
addparam(char *name)
{
long index; /* current symbol index */
index = findstr(&paramnames, name);
if (index >= 0)
return index;
index = paramnames.h_count;
(void) addstr(&paramnames, name);
curfunc->f_paramcount++;
return index;
}
/*
* Find a parameter variable name and return its index.
* Returns minus one if the variable name is not defined.
*
* given:
* name name of parameter variable
*/
long
findparam(char *name)
{
return findstr(&paramnames, name);
}
/*
* Return the name of a parameter variable.
*/
char *
paramname(long n)
{
return namestr(&paramnames, n);
}
/*
* Return the type of a variable name.
* This is either local, parameter, global, static, or undefined.
*
* given:
* name variable name to find
*/
int
symboltype(char *name)
{
GLOBAL *sp;
if (findparam(name) >= 0)
return SYM_PARAM;
if (findlocal(name) >= 0)
return SYM_LOCAL;
sp = findglobal(name);
if (sp) {
if (sp->g_filescope == SCOPE_GLOBAL)
return SYM_GLOBAL;
return SYM_STATIC;
}
return SYM_UNDEFINED;
}
/* END CODE */