(hash_ask): Call strcmp instead of expanding it inline.

(hash_code): Replaced with a version from bfd.
This commit is contained in:
Ken Raeburn
1995-01-18 18:52:08 +00:00
parent 958998b7aa
commit 95ac738e70

View File

@ -1,5 +1,5 @@
/* hash.c - hash table lookup strings - /* hash.c - hash table lookup strings -
Copyright (C) 1987, 1990, 1991 Free Software Foundation, Inc. Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler. This file is part of GAS, the GNU Assembler.
@ -67,12 +67,13 @@
/* /*
* The code and its structures are re-enterent. * The code and its structures are re-enterent.
*
* Before you do anything else, you must call hash_new() which will * Before you do anything else, you must call hash_new() which will
* return the address of a hash-table-control-block (or NULL if there * return the address of a hash-table-control-block. You then use
* is not enough memory). You then use this address as a handle of the * this address as a handle of the symbol table by passing it to all
* symbol table by passing it to all the other hash_...() functions. * the other hash_...() functions. The only approved way to recover
* The only approved way to recover the memory used by the symbol table * the memory used by the symbol table is to call hash_die() with the
* is to call hash_die() with the handle of the symbol table. * handle of the symbol table.
* *
* Before you call hash_die() you normally delete anything pointed to * Before you call hash_die() you normally delete anything pointed to
* by individual symbols. After hash_die() you can't use that symbol * by individual symbols. After hash_die() you can't use that symbol
@ -103,8 +104,7 @@
* (total hashes,collisions) for (reads,writes) (*) * (total hashes,collisions) for (reads,writes) (*)
* All of the above values vary in time. * All of the above values vary in time.
* (*) some of these numbers will not be meaningful if we change the * (*) some of these numbers will not be meaningful if we change the
* internals. * internals. */
*/
/* /*
* I N T E R N A L * I N T E R N A L
@ -136,23 +136,21 @@
#define error as_fatal #define error as_fatal
#define DELETED ((char *)1) /* guarenteed invalid address */ #define DELETED ((PTR)1) /* guarenteed invalid address */
#define START_POWER (11) /* power of two: size of new hash table *//* JF was 6 */ #define START_POWER (11) /* power of two: size of new hash table */
/* JF These next two aren't used any more. */
/* #define START_SIZE (64) / * 2 ** START_POWER */
/* #define START_FULL (32) / * number of entries before table expands */
#define islive(ptr) (ptr->hash_string && ptr->hash_string!=DELETED)
/* above TRUE if a symbol is in entry @ ptr */
#define STAT_SIZE (0) /* number of slots in hash table */ /* TRUE if a symbol is in entry @ ptr. */
/* the wall does not count here */ #define islive(ptr) (ptr->hash_string && ptr->hash_string!=DELETED)
/* we expect this is always a power of 2 */
/* Number of slots in hash table. The wall does not count here.
We expect this is always a power of 2. */
#define STAT_SIZE (0)
#define STAT_ACCESS (1) /* number of hash_ask()s */ #define STAT_ACCESS (1) /* number of hash_ask()s */
#define STAT__READ (0) /* reading */ #define STAT__READ (0) /* reading */
#define STAT__WRITE (1) /* writing */ #define STAT__WRITE (1) /* writing */
#define STAT_COLLIDE (3) /* number of collisions (total) */ /* Number of collisions (total). This may exceed STAT_ACCESS if we
/* this may exceed STAT_ACCESS if we have */ have lots of collisions/access. */
/* lots of collisions/access */ #define STAT_COLLIDE (3)
#define STAT_USED (5) /* slots used right now */ #define STAT_USED (5) /* slots used right now */
#define STATLENGTH (6) /* size of statistics block */ #define STATLENGTH (6) /* size of statistics block */
#if STATLENGTH != HASH_STATLENGTH #if STATLENGTH != HASH_STATLENGTH
@ -162,7 +160,8 @@ Panic! Please make #include "stat.h" agree with previous definitions!
/* #define SUSPECT to do runtime checks */ /* #define SUSPECT to do runtime checks */
/* #define TEST to be a test jig for hash...() */ /* #define TEST to be a test jig for hash...() */
#ifdef TEST /* TEST: use smaller hash table */ #ifdef TEST
/* TEST: use smaller hash table */
#undef START_POWER #undef START_POWER
#define START_POWER (3) #define START_POWER (3)
#undef START_SIZE #undef START_SIZE
@ -180,7 +179,7 @@ Panic! Please make #include "stat.h" agree with previous definitions!
char * s; symbol string (address) [ key ] char * s; symbol string (address) [ key ]
char * v; value string (address) [datum] char * v; value string (address) [datum]
boolean f; TRUE if we found s in hash table i boolean f; TRUE if we found s in hash table i
char * t; error string; "" means OK char * t; error string; 0 means OK
int a; access type [0...n) i int a; access type [0...n) i
c=hash_new () create new hash_control c=hash_new () create new hash_control
@ -229,47 +228,37 @@ Panic! Please make #include "stat.h" agree with previous definitions!
*/ */
static char hash_found; /* returned by hash_ask() to stop extra */ /* Returned by hash_ask() to stop extra testing. hash_ask() wants to
/* testing. hash_ask() wants to return both */ return both a slot and a status. This is the status. TRUE: found
/* a slot and a status. This is the status. */ symbol FALSE: absent: empty or deleted slot Also returned by
/* TRUE: found symbol */ hash_jam(). TRUE: we replaced a value FALSE: we inserted a value. */
/* FALSE: absent: empty or deleted slot */ static char hash_found;
/* Also returned by hash_jam(). */
/* TRUE: we replaced a value */
/* FALSE: we inserted a value */
static struct hash_entry * hash_ask(); static struct hash_entry *hash_ask PARAMS ((struct hash_control *,
static int hash_code (); const char *, int));
static char * hash_grow(); static int hash_code PARAMS ((struct hash_control *, const char *));
static const char *hash_grow PARAMS ((struct hash_control *));
/* /* Create a new hash table. Return NULL if failed; otherwise return handle
* h a s h _ n e w ( ) (address of struct hash). */
*
*/
struct hash_control * struct hash_control *
hash_new() /* create a new hash table */ hash_new ()
/* return NULL if failed */
/* return handle (address of struct hash) */
{ {
register struct hash_control * retval; struct hash_control *retval;
register struct hash_entry * room; /* points to hash table */ struct hash_entry *room; /* points to hash table */
register struct hash_entry * wall; struct hash_entry *wall;
register struct hash_entry * entry; struct hash_entry *entry;
register int * ip; /* scan stats block of struct hash_control */ int *ip; /* scan stats block of struct hash_control */
register int * nd; /* limit of stats block */ int *nd; /* limit of stats block */
if (( room = (struct hash_entry *) malloc( sizeof(struct room = (struct hash_entry *) xmalloc (sizeof (struct hash_entry)
hash_entry)*((1<<START_POWER) + 1) ) ) != NULL)
/* +1 for the wall entry */ /* +1 for the wall entry */
{ * ((1 << START_POWER) + 1));
if (( retval = (struct hash_control *) malloc(sizeof(struct retval = (struct hash_control *) xmalloc (sizeof (struct hash_control));
hash_control)) ) != NULL)
{
nd = retval->hash_stat + STATLENGTH; nd = retval->hash_stat + STATLENGTH;
for (ip = retval->hash_stat; ip < nd; ip++) for (ip = retval->hash_stat; ip < nd; ip++)
{
*ip = 0; *ip = 0;
}
retval->hash_stat[STAT_SIZE] = 1 << START_POWER; retval->hash_stat[STAT_SIZE] = 1 << START_POWER;
retval->hash_mask = (1 << START_POWER) - 1; retval->hash_mask = (1 << START_POWER) - 1;
@ -280,16 +269,8 @@ struct hash_control *
wall = room + (1 << START_POWER); wall = room + (1 << START_POWER);
retval->hash_full = (1 << START_POWER) / 2; retval->hash_full = (1 << START_POWER) / 2;
for (entry = room; entry <= wall; entry++) for (entry = room; entry <= wall; entry++)
{
entry->hash_string = NULL; entry->hash_string = NULL;
} return retval;
}
}
else
{
retval = NULL; /* no room for table: fake a failure */
}
return(retval); /* return NULL or set-up structs */
} }
/* /*
@ -325,12 +306,12 @@ struct hash_control * handle;
*/ */
void void
hash_say (handle, buffer, bufsiz) hash_say (handle, buffer, bufsiz)
register struct hash_control * handle; struct hash_control *handle;
register int buffer[/*bufsiz*/]; int buffer[ /*bufsiz*/ ];
register int bufsiz; int bufsiz;
{ {
register int * nd; /* limit of statistics block */ int *nd; /* limit of statistics block */
register int * ip; /* scan statistics */ int *ip; /* scan statistics */
ip = handle->hash_stat; ip = handle->hash_stat;
nd = ip + min (bufsiz - 1, STATLENGTH); nd = ip + min (bufsiz - 1, STATLENGTH);
@ -353,21 +334,21 @@ register int bufsiz;
* Anyway, the symbol is not present after this function. * Anyway, the symbol is not present after this function.
* *
*/ */
char * /* NULL if string not in table, else */ PTR /* NULL if string not in table, else */
/* returns value of deleted symbol */ /* returns value of deleted symbol */
hash_delete (handle, string) hash_delete (handle, string)
register struct hash_control * handle; struct hash_control *handle;
register char * string; const char *string;
{ {
register char * retval; /* NULL if string not in table */ PTR retval;
register struct hash_entry * entry; /* NULL or entry of this symbol */ struct hash_entry *entry;
entry = hash_ask (handle, string, STAT__WRITE); entry = hash_ask (handle, string, STAT__WRITE);
if (hash_found) if (hash_found)
{ {
retval = entry->hash_value; retval = entry->hash_value;
entry -> hash_string = DELETED; /* mark as deleted */ entry->hash_string = DELETED;
handle -> hash_stat[STAT_USED] -= 1; /* slots-in-use count */ handle->hash_stat[STAT_USED] -= 1;
#ifdef SUSPECT #ifdef SUSPECT
if (handle->hash_stat[STAT_USED] < 0) if (handle->hash_stat[STAT_USED] < 0)
{ {
@ -390,14 +371,14 @@ register char * string;
* Return NULL and don't change the table if the symbol is not already * Return NULL and don't change the table if the symbol is not already
* in the table. * in the table.
*/ */
char * PTR
hash_replace (handle, string, value) hash_replace (handle, string, value)
register struct hash_control * handle; struct hash_control *handle;
register char * string; const char *string;
register char * value; PTR value;
{ {
register struct hash_entry * entry; struct hash_entry *entry;
register char * retval; char *retval;
entry = hash_ask (handle, string, STAT__WRITE); entry = hash_ask (handle, string, STAT__WRITE);
if (hash_found) if (hash_found)
@ -410,32 +391,32 @@ register char * value;
retval = NULL; retval = NULL;
} }
; ;
return (retval); return retval;
} }
/* /*
* h a s h _ i n s e r t ( ) * h a s h _ i n s e r t ( )
* *
* Insert a (symbol-string, value) into the hash table. * Insert a (symbol-string, value) into the hash table.
* Return an error string, "" means OK. * Return an error string, 0 means OK.
* It is an 'error' to insert an existing symbol. * It is an 'error' to insert an existing symbol.
*/ */
char * /* return error string */ const char * /* return error string */
hash_insert (handle, string, value) hash_insert (handle, string, value)
register struct hash_control * handle; struct hash_control *handle;
register char * string; const char *string;
register char * value; PTR value;
{ {
register struct hash_entry * entry; struct hash_entry *entry;
register char * retval; const char *retval;
retval = ""; retval = 0;
if (handle->hash_stat[STAT_USED] > handle->hash_full) if (handle->hash_stat[STAT_USED] > handle->hash_full)
{ {
retval = hash_grow (handle); retval = hash_grow (handle);
} }
if ( ! * retval) if (!retval)
{ {
entry = hash_ask (handle, string, STAT__WRITE); entry = hash_ask (handle, string, STAT__WRITE);
if (hash_found) if (hash_found)
@ -449,7 +430,7 @@ register char * value;
handle->hash_stat[STAT_USED] += 1; handle->hash_stat[STAT_USED] += 1;
} }
} }
return(retval); return retval;
} }
/* /*
@ -458,7 +439,7 @@ register char * value;
* Regardless of what was in the symbol table before, after hash_jam() * Regardless of what was in the symbol table before, after hash_jam()
* the named symbol has the given value. The symbol is either inserted or * the named symbol has the given value. The symbol is either inserted or
* (its value is) relpaced. * (its value is) relpaced.
* An error message string is returned, "" means OK. * An error message string is returned, 0 means OK.
* *
* WARNING: this may decide to grow the hashed symbol table. * WARNING: this may decide to grow the hashed symbol table.
* To do this, we call hash_grow(), WHICH WILL recursively CALL US. * To do this, we call hash_grow(), WHICH WILL recursively CALL US.
@ -466,21 +447,21 @@ register char * value;
* We report status internally: hash_found is TRUE if we replaced, but * We report status internally: hash_found is TRUE if we replaced, but
* false if we inserted. * false if we inserted.
*/ */
char * const char *
hash_jam (handle, string, value) hash_jam (handle, string, value)
register struct hash_control * handle; struct hash_control *handle;
register char * string; const char *string;
register char * value; PTR value;
{ {
register char * retval; const char *retval;
register struct hash_entry * entry; struct hash_entry *entry;
retval = ""; retval = 0;
if (handle->hash_stat[STAT_USED] > handle->hash_full) if (handle->hash_stat[STAT_USED] > handle->hash_full)
{ {
retval = hash_grow (handle); retval = hash_grow (handle);
} }
if (! * retval) if (!retval)
{ {
entry = hash_ask (handle, string, STAT__WRITE); entry = hash_ask (handle, string, STAT__WRITE);
if (!hash_found) if (!hash_found)
@ -490,7 +471,7 @@ register char * value;
} }
entry->hash_value = value; entry->hash_value = value;
} }
return(retval); return retval;
} }
/* /*
@ -498,26 +479,26 @@ register char * value;
* *
* Grow a new (bigger) hash table from the old one. * Grow a new (bigger) hash table from the old one.
* We choose to double the hash table's size. * We choose to double the hash table's size.
* Return a human-scrutible error string: "" if OK. * Return a human-scrutible error string: 0 if OK.
* Warning! This uses hash_jam(), which had better not recurse * Warning! This uses hash_jam(), which had better not recurse
* back here! Hash_jam() conditionally calls us, but we ALWAYS * back here! Hash_jam() conditionally calls us, but we ALWAYS
* call hash_jam()! * call hash_jam()!
* Internal. * Internal.
*/ */
static char * static const char *
hash_grow (handle) /* make a hash table grow */ hash_grow (handle) /* make a hash table grow */
struct hash_control *handle; struct hash_control *handle;
{ {
register struct hash_entry * newwall; struct hash_entry *newwall;
register struct hash_entry * newwhere; struct hash_entry *newwhere;
struct hash_entry *newtrack; struct hash_entry *newtrack;
register struct hash_entry * oldtrack; struct hash_entry *oldtrack;
register struct hash_entry * oldwhere; struct hash_entry *oldwhere;
register struct hash_entry * oldwall; struct hash_entry *oldwall;
register int temp; int temp;
int newsize; int newsize;
char * string; const char *string;
char * retval; const char *retval;
#ifdef SUSPECT #ifdef SUSPECT
int oldused; int oldused;
#endif #endif
@ -534,11 +515,13 @@ struct hash_control * handle;
* attempt to get enough room for a hash table twice as big * attempt to get enough room for a hash table twice as big
*/ */
temp = handle->hash_stat[STAT_SIZE]; temp = handle->hash_stat[STAT_SIZE];
if (( newwhere = (struct hash_entry *) if ((newwhere = ((struct hash_entry *)
xmalloc((long)((temp+temp+1)*sizeof(struct hash_entry)))) != NULL) xmalloc ((unsigned long) ((temp + temp + 1)
* sizeof (struct hash_entry)))))
!= NULL)
/* +1 for wall slot */ /* +1 for wall slot */
{ {
retval = ""; /* assume success until proven otherwise */ retval = 0; /* assume success until proven otherwise */
/* /*
* have enough room: now we do all the work. * have enough room: now we do all the work.
* double the size of everything in handle, * double the size of everything in handle,
@ -566,22 +549,17 @@ struct hash_control * handle;
*/ */
handle->hash_stat[STAT_USED] = 0; /* inserts will bump it up to correct */ handle->hash_stat[STAT_USED] = 0; /* inserts will bump it up to correct */
for (oldtrack = oldwhere; oldtrack < oldwall; oldtrack++) for (oldtrack = oldwhere; oldtrack < oldwall; oldtrack++)
{
if (((string = oldtrack->hash_string) != NULL) && string != DELETED) if (((string = oldtrack->hash_string) != NULL) && string != DELETED)
{ if ((retval = hash_jam (handle, string, oldtrack->hash_value)))
if ( * (retval = hash_jam(handle,string,oldtrack->hash_value) ) )
{
break; break;
}
}
}
#ifdef SUSPECT #ifdef SUSPECT
if ( !*retval && handle->hash_stat[STAT_USED] != oldused) if (!retval && handle->hash_stat[STAT_USED] != oldused)
{ {
retval = "hash_used"; retval = "hash_used";
} }
#endif #endif
if (!*retval) if (!retval)
{ {
/* /*
* we have a completely faked up control block. * we have a completely faked up control block.
@ -589,7 +567,7 @@ struct hash_control * handle;
*/ */
free ((char *) oldwhere); free ((char *) oldwhere);
/* /*
* Here with success. retval is already "". * Here with success. retval is already 0.
*/ */
} }
} }
@ -597,7 +575,7 @@ struct hash_control * handle;
{ {
retval = "no room"; retval = "no room";
} }
return(retval); return retval;
} }
/* /*
@ -651,8 +629,8 @@ char *
struct hash_control *handle; struct hash_control *handle;
char *(*function) (); char *(*function) ();
{ {
register struct hash_entry * entry; struct hash_entry *entry;
register struct hash_entry * wall; struct hash_entry *wall;
wall = handle->hash_wall; wall = handle->hash_wall;
for (entry = handle->hash_where; entry < wall; entry++) for (entry = handle->hash_where; entry < wall; entry++)
@ -671,24 +649,18 @@ char* (*function)();
* Given symbol string, find value (if any). * Given symbol string, find value (if any).
* Return found value or NULL. * Return found value or NULL.
*/ */
char * PTR
hash_find(handle,string) /* return char* or NULL */ hash_find (handle, string)
struct hash_control *handle; struct hash_control *handle;
char * string; const char *string;
{ {
register struct hash_entry * entry; struct hash_entry *entry;
register char * retval;
entry = hash_ask (handle, string, STAT__READ); entry = hash_ask (handle, string, STAT__READ);
if (hash_found) if (hash_found)
{ return entry->hash_value;
retval = entry->hash_value;
}
else else
{ return NULL;
retval = NULL;
}
return(retval);
} }
/* /*
@ -703,23 +675,31 @@ char * string;
static struct hash_entry * /* string slot, may be empty or deleted */ static struct hash_entry * /* string slot, may be empty or deleted */
hash_ask (handle, string, access) hash_ask (handle, string, access)
struct hash_control *handle; struct hash_control *handle;
char * string; const char *string;
int access; /* access type */ int access; /* access type */
{ {
register char *string1; /* JF avoid strcmp calls */ const char *string1; /* JF avoid strcmp calls */
register char * s; const char *s;
register int c; int c;
register struct hash_entry * slot; struct hash_entry *slot;
register int collision; /* count collisions */ int collision; /* count collisions */
/* start looking here */
slot = handle->hash_where + hash_code (handle, string);
slot = handle->hash_where + hash_code(handle,string); /* start looking here */
handle->hash_stat[STAT_ACCESS + access] += 1; handle->hash_stat[STAT_ACCESS + access] += 1;
collision = 0; collision = 0;
hash_found = FALSE; hash_found = FALSE;
while (((s = slot->hash_string) != NULL) && s != DELETED) while (((s = slot->hash_string) != NULL) && s != DELETED)
{ {
for(string1=string;;) { #if 1
if((c= *s++) == 0) { if (string == s || !strcmp (string, s))
hash_found = TRUE;
#else
for (string1 = string;;)
{
if ((c = *s++) == 0)
{
if (!*string1) if (!*string1)
hash_found = TRUE; hash_found = TRUE;
break; break;
@ -727,6 +707,7 @@ int access; /* access type */
if (*string1++ != c) if (*string1++ != c)
break; break;
} }
#endif
if (hash_found) if (hash_found)
break; break;
collision++; collision++;
@ -745,14 +726,21 @@ int access; /* access type */
slot = handle->hash_where;/* now look again */ slot = handle->hash_where;/* now look again */
while (((s = slot->hash_string) != NULL) && s != DELETED) while (((s = slot->hash_string) != NULL) && s != DELETED)
{ {
for(string1=string;*s;string1++,s++) { #if 0
for (string1 = string; *s; string1++, s++)
{
if (*string1 != *s) if (*string1 != *s)
break; break;
} }
if(*s==*string1) { if (*s == *string1)
{
hash_found = TRUE; hash_found = TRUE;
break; break;
} }
#else
if (string == s || !strcmp (string, s))
hash_found = TRUE;
#endif
collision++; collision++;
slot++; slot++;
} }
@ -764,7 +752,6 @@ int access; /* access type */
* DELETED:dig here slot * DELETED:dig here slot
*/ */
} }
/* fprintf(stderr,"hash_ask(%s)->%d(%d)\n",string,hash_code(handle,string),collision); */
handle->hash_stat[STAT_COLLIDE + access] += collision; handle->hash_stat[STAT_COLLIDE + access] += collision;
return (slot); /* also return hash_found */ return (slot); /* also return hash_found */
} }
@ -778,11 +765,12 @@ int access; /* access type */
static int static int
hash_code (handle, string) hash_code (handle, string)
struct hash_control *handle; struct hash_control *handle;
register char * string; const char *string;
{ {
register long h; /* hash code built here */ #if 0
register long c; /* each character lands here */ long h; /* hash code built here */
register int n; /* Amount to shift h by */ long c; /* each character lands here */
int n; /* Amount to shift h by */
n = (handle->hash_sizelog - 3); n = (handle->hash_sizelog - 3);
h = 0; h = 0;
@ -792,6 +780,21 @@ register char * string;
h = (h << 3) + (h >> n) + c; h = (h << 3) + (h >> n) + c;
} }
return (h & handle->hash_mask); return (h & handle->hash_mask);
#else
unsigned long h = 0;
unsigned int len = 0;
unsigned int c;
while ((c = *string++) != 0)
{
h += c + (c << 17);
h ^= h >> 2;
++len;
}
h += len + (len << 17);
h ^= h >> 2;
return h & handle->hash_mask;
#endif
} }
/* /*
@ -820,11 +823,8 @@ int number; /* number 0:TABLES-1 of current hashed */
main () main ()
{ {
char (*applicatee ()); char (*applicatee ());
char * hash_find();
char *destroy (); char *destroy ();
char *what (); char *what ();
struct hash_control * hash_new();
char * hash_replace();
int *ip; int *ip;
number = 0; number = 0;
@ -835,7 +835,8 @@ main()
printf ("hash_test command: "); printf ("hash_test command: ");
gets (answer); gets (answer);
command = answer[0]; command = answer[0];
if (isupper(command)) command = tolower(command); /* ecch! */ if (isupper (command))
command = tolower (command); /* ecch! */
switch (command) switch (command)
{ {
case '#': case '#':
@ -877,14 +878,15 @@ main()
break; break;
case 'i': case 'i':
p = hash_insert (h, name = what ("symbol"), value = what ("value")); p = hash_insert (h, name = what ("symbol"), value = what ("value"));
if (*p) if (p)
{ {
printf("symbol=\"%s\" value=\"%s\" error=%s\n",name,value,p); printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value,
p);
} }
break; break;
case 'j': case 'j':
p = hash_jam (h, name = what ("symbol"), value = what ("value")); p = hash_jam (h, name = what ("symbol"), value = what ("value"));
if (*p) if (p)
{ {
printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, p); printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, p);
} }
@ -893,7 +895,7 @@ main()
h = hashtable[number] = (char *) hash_new (); h = hashtable[number] = (char *) hash_new ();
break; break;
case 'q': case 'q':
exit(); exit (EXIT_SUCCESS);
case 'r': case 'r':
p = hash_replace (h, name = what ("symbol"), value = what ("value")); p = hash_replace (h, name = what ("symbol"), value = what ("value"));
printf ("old value was \"%s\"\n", p ? p : "{}"); printf ("old value was \"%s\"\n", p ? p : "{}");