/* * Atom table functions * * Copyright 1993, 1994, 1995 Alexandre Julliard */ /* * Warning: The code assumes that LocalAlloc() returns a block aligned * on a 4-bytes boundary (because of the shifting done in * HANDLETOATOM). If this is not the case, the allocation code will * have to be changed. */ #include #include #include #include #include "wine/winbase16.h" #include "wine/winuser16.h" #include "winuser.h" #include "global.h" #include "instance.h" #include "ldt.h" #include "stackframe.h" #include "user.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(atom) #define DEFAULT_ATOMTABLE_SIZE 37 #define MIN_STR_ATOM 0xc000 #define MAX_ATOM_LEN 255 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2) #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2))) #define HAS_ATOM_TABLE(sel) \ ((INSTANCEDATA*)PTR_SEG_OFF_TO_LIN(sel,0))->atomtable != 0) #define GET_ATOM_TABLE(sel) ((ATOMTABLE*)PTR_SEG_OFF_TO_LIN(sel, \ ((INSTANCEDATA*)PTR_SEG_OFF_TO_LIN(sel,0))->atomtable)) typedef struct { HANDLE16 next; WORD refCount; BYTE length; BYTE str[1]; } ATOMENTRY; typedef struct { WORD size; HANDLE16 entries[1]; } ATOMTABLE; static WORD ATOM_GlobalTable = 0; /*********************************************************************** * ATOM_InitTable * * NOTES * Should this validate the value of entries to be 0 < x < 0x3fff? * * RETURNS * Handle: Success * 0: Failure */ static HANDLE16 ATOM_InitTable( WORD selector, /* [in] Segment */ WORD entries /* [in] Size of atom table */ ) { int i; HANDLE16 handle; ATOMTABLE *table; /* We consider the first table to be initialized as the global table. * This works, as USER (both built-in and native) is the first one to * register ... */ if (!ATOM_GlobalTable) ATOM_GlobalTable = selector; /* Allocate the table */ handle = LOCAL_Alloc( selector, LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) ); if (!handle) return 0; table = (ATOMTABLE *)PTR_SEG_OFF_TO_LIN( selector, handle ); table->size = entries; for (i = 0; i < entries; i++) table->entries[i] = 0; /* Store a pointer to the table in the instance data */ ((INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 ))->atomtable = handle; return handle; } /*********************************************************************** * ATOM_Init * * Global table initialisation. */ BOOL ATOM_Init( WORD globalTableSel ) { return ATOM_InitTable( globalTableSel, DEFAULT_ATOMTABLE_SIZE ) != 0; } /*********************************************************************** * ATOM_GetTable * * Return a pointer to the atom table of a given segment, creating * it if necessary. * * RETURNS * Pointer to table: Success * NULL: Failure */ static ATOMTABLE *ATOM_GetTable( WORD selector, /* [in] Segment */ BOOL create /* [in] Create */ ) { INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 ); if (ptr->atomtable) { ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable); if (table->size) return table; } if (!create) return NULL; if (!ATOM_InitTable( selector, DEFAULT_ATOMTABLE_SIZE )) return NULL; /* Reload ptr in case it moved in linear memory */ ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 ); return (ATOMTABLE *)((char *)ptr + ptr->atomtable); } /*********************************************************************** * ATOM_MakePtr * * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()). */ static ATOMENTRY *ATOM_MakePtr( WORD selector, /* [in] Segment */ HANDLE16 handle /* [in] Handle */ ) { return (ATOMENTRY *)PTR_SEG_OFF_TO_LIN( selector, handle ); } /*********************************************************************** * ATOM_Hash * RETURNS * The hash value for the input string */ static WORD ATOM_Hash( WORD entries, /* [in] Total number of entries */ LPCSTR str, /* [in] Pointer to string to hash */ WORD len /* [in] Length of string */ ) { WORD i, hash = 0; TRACE("%x, %s, %x\n", entries, str, len); for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i; return hash % entries; } static BOOL ATOM_IsIntAtom(LPCSTR atomstr,WORD *atomid) { LPSTR xend; if (!HIWORD(atomstr)) { *atomid = LOWORD(atomstr); return TRUE; } if (atomstr[0]!='#') return FALSE; *atomid=strtol(atomstr+1,&xend,10); if (*xend) { /* atom has a name like '## some name ##' */ return FALSE; } return TRUE; } /*********************************************************************** * ATOM_AddAtom * * Windows DWORD aligns the atom entry size. * The remaining unused string space created by the alignment * gets padded with '\0's in a certain way to ensure * that at least one trailing '\0' remains. * * RETURNS * Atom: Success * 0: Failure */ static ATOM ATOM_AddAtom( WORD selector, /* [in] Segment */ LPCSTR str /* [in] Pointer to the string to add */ ) { WORD hash; HANDLE16 entry; ATOMENTRY * entryPtr; ATOMTABLE * table; int len, ae_len; WORD iatom; TRACE("0x%x, %s\n", selector, str); if (ATOM_IsIntAtom(str,&iatom)) return iatom; if ((len = strlen( str )) > MAX_ATOM_LEN) len = MAX_ATOM_LEN; if (!(table = ATOM_GetTable( selector, TRUE ))) return 0; hash = ATOM_Hash( table->size, str, len ); entry = table->entries[hash]; while (entry) { entryPtr = ATOM_MakePtr( selector, entry ); if ((entryPtr->length == len) && (!lstrncmpiA( entryPtr->str, str, len ))) { entryPtr->refCount++; TRACE("-- existing 0x%x\n", entry); return HANDLETOATOM( entry ); } entry = entryPtr->next; } ae_len = (sizeof(ATOMENTRY)+len+3) & ~3; entry = LOCAL_Alloc( selector, LMEM_FIXED, ae_len); if (!entry) return 0; /* Reload the table ptr in case it moved in linear memory */ table = ATOM_GetTable( selector, FALSE ); entryPtr = ATOM_MakePtr( selector, entry ); entryPtr->next = table->entries[hash]; entryPtr->refCount = 1; entryPtr->length = len; /* Some applications _need_ the '\0' padding provided by this strncpy */ strncpy( entryPtr->str, str, ae_len - sizeof(ATOMENTRY) + 1 ); entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0'; table->entries[hash] = entry; TRACE("-- new 0x%x\n", entry); return HANDLETOATOM( entry ); } /*********************************************************************** * ATOM_DeleteAtom * RETURNS * 0: Success * Atom: Failure */ static ATOM ATOM_DeleteAtom( WORD selector, /* [in] Segment */ ATOM atom /* [in] Atom to delete */ ) { ATOMENTRY * entryPtr; ATOMTABLE * table; HANDLE16 entry, *prevEntry; WORD hash; TRACE("0x%x, 0x%x\n", selector, atom); if (atom < MIN_STR_ATOM) return 0; /* Integer atom */ if (!(table = ATOM_GetTable( selector, FALSE ))) return 0; entry = ATOMTOHANDLE( atom ); entryPtr = ATOM_MakePtr( selector, entry ); /* Find previous atom */ hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length ); prevEntry = &table->entries[hash]; while (*prevEntry && *prevEntry != entry) { ATOMENTRY * prevEntryPtr = ATOM_MakePtr( selector, *prevEntry ); prevEntry = &prevEntryPtr->next; } if (!*prevEntry) return atom; /* Delete atom */ if (--entryPtr->refCount == 0) { *prevEntry = entryPtr->next; LOCAL_Free( selector, entry ); } return 0; } /*********************************************************************** * ATOM_FindAtom * RETURNS * Atom: Success * 0: Failure */ static ATOM ATOM_FindAtom( WORD selector, /* [in] Segment */ LPCSTR str /* [in] Pointer to string to find */ ) { ATOMTABLE * table; WORD hash,iatom; HANDLE16 entry; int len; TRACE("%x, %s\n", selector, str); if (ATOM_IsIntAtom(str,&iatom)) return iatom; if ((len = strlen( str )) > 255) len = 255; if (!(table = ATOM_GetTable( selector, FALSE ))) return 0; hash = ATOM_Hash( table->size, str, len ); entry = table->entries[hash]; while (entry) { ATOMENTRY * entryPtr = ATOM_MakePtr( selector, entry ); if ((entryPtr->length == len) && (!lstrncmpiA( entryPtr->str, str, len ))) { TRACE("-- found %x\n", entry); return HANDLETOATOM( entry ); } entry = entryPtr->next; } TRACE("-- not found\n"); return 0; } /*********************************************************************** * ATOM_GetAtomName * RETURNS * Length of string copied to buffer: Success * 0: Failure */ static UINT ATOM_GetAtomName( WORD selector, /* [in] Segment */ ATOM atom, /* [in] Atom identifier */ LPSTR buffer, /* [out] Pointer to buffer for atom string */ INT count /* [in] Size of buffer */ ) { ATOMTABLE * table; ATOMENTRY * entryPtr; HANDLE16 entry; char * strPtr; UINT len; char text[8]; TRACE("%x, %x\n", selector, atom); if (!count) return 0; if (atom < MIN_STR_ATOM) { sprintf( text, "#%d", atom ); len = strlen(text); strPtr = text; } else { if (!(table = ATOM_GetTable( selector, FALSE ))) return 0; entry = ATOMTOHANDLE( atom ); entryPtr = ATOM_MakePtr( selector, entry ); len = entryPtr->length; strPtr = entryPtr->str; } if (len >= count) len = count-1; memcpy( buffer, strPtr, len ); buffer[len] = '\0'; return len; } /*********************************************************************** * InitAtomTable16 (KERNEL.68) */ WORD WINAPI InitAtomTable16( WORD entries ) { if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */ return ATOM_InitTable( CURRENT_DS, entries ); } /*********************************************************************** * GetAtomHandle (KERNEL.73) */ HANDLE16 WINAPI GetAtomHandle16( ATOM atom ) { if (atom < MIN_STR_ATOM) return 0; return ATOMTOHANDLE( atom ); } /*********************************************************************** * AddAtom16 (KERNEL.70) */ ATOM WINAPI AddAtom16( SEGPTR str ) { ATOM atom; HANDLE16 ds = CURRENT_DS; if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ if (SELECTOR_TO_ENTRY(LOWORD(str)) == SELECTOR_TO_ENTRY(ds)) { /* If the string is in the same data segment as the atom table, make */ /* a copy of the string to be sure it doesn't move in linear memory. */ char buffer[MAX_ATOM_LEN+1]; lstrcpynA( buffer, (char *)PTR_SEG_TO_LIN(str), sizeof(buffer) ); atom = ATOM_AddAtom( ds, buffer ); } else atom = ATOM_AddAtom( ds, (LPCSTR)PTR_SEG_TO_LIN(str) ); return atom; } /*********************************************************************** * AddAtom32A (KERNEL32.0) * Adds a string to the atom table and returns the atom identifying the * string. * * RETURNS * Atom: Success * 0: Failure */ ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ ) { return GlobalAddAtomA( str ); /* FIXME */ } /*********************************************************************** * AddAtom32W (KERNEL32.1) * See AddAtom32A */ ATOM WINAPI AddAtomW( LPCWSTR str ) { return GlobalAddAtomW( str ); /* FIXME */ } /*********************************************************************** * DeleteAtom16 (KERNEL.71) */ ATOM WINAPI DeleteAtom16( ATOM atom ) { return ATOM_DeleteAtom( CURRENT_DS, atom ); } /*********************************************************************** * DeleteAtom32 (KERNEL32.69) * Decrements the reference count of a string atom. If count becomes * zero, the string associated with the atom is removed from the table. * * RETURNS * 0: Success * Atom: Failure */ ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ ) { return GlobalDeleteAtom( atom ); /* FIXME */ } /*********************************************************************** * FindAtom16 (KERNEL.69) */ ATOM WINAPI FindAtom16( SEGPTR str ) { if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ return ATOM_FindAtom( CURRENT_DS, (LPCSTR)PTR_SEG_TO_LIN(str) ); } /*********************************************************************** * FindAtom32A (KERNEL32.117) * Searches the local atom table for the string and returns the atom * associated with that string. * * RETURNS * Atom: Success * 0: Failure */ ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ ) { return GlobalFindAtomA( str ); /* FIXME */ } /*********************************************************************** * FindAtom32W (KERNEL32.118) * See FindAtom32A */ ATOM WINAPI FindAtomW( LPCWSTR str ) { return GlobalFindAtomW( str ); /* FIXME */ } /*********************************************************************** * GetAtomName16 (KERNEL.72) */ UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count ) { return (UINT16)ATOM_GetAtomName( CURRENT_DS, atom, buffer, count ); } /*********************************************************************** * GetAtomName32A (KERNEL32.149) * Retrieves a copy of the string associated with the atom. * * RETURNS * Length of string: Success * 0: Failure */ UINT WINAPI GetAtomNameA( ATOM atom, /* [in] Atom */ LPSTR buffer, /* [out] Pointer to string for atom string */ INT count /* [in] Size of buffer */ ) { return GlobalGetAtomNameA( atom, buffer, count ); /* FIXME */ } /*********************************************************************** * GetAtomName32W (KERNEL32.150) * See GetAtomName32A */ UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count ) { return GlobalGetAtomNameW( atom, buffer, count ); /* FIXME */ } /*********************************************************************** * GlobalAddAtom16 (USER.268) */ ATOM WINAPI GlobalAddAtom16( SEGPTR str ) { if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ return ATOM_AddAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) ); } /*********************************************************************** * GlobalAddAtom32A (KERNEL32.313) * Adds a character string to the global atom table and returns a unique * value identifying the string. * * RETURNS * Atom: Success * 0: Failure */ ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ ) { if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ return ATOM_AddAtom( ATOM_GlobalTable, str ); } /*********************************************************************** * GlobalAddAtom32W (KERNEL32.314) * See GlobalAddAtom32A */ ATOM WINAPI GlobalAddAtomW( LPCWSTR str ) { char buffer[MAX_ATOM_LEN+1]; if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ lstrcpynWtoA( buffer, str, sizeof(buffer) ); return ATOM_AddAtom( ATOM_GlobalTable, buffer ); } /*********************************************************************** * GlobalDeleteAtom (USER.269) (KERNEL32.317) * Decrements the reference count of a string atom. If the count is * zero, the string associated with the atom is removed from the table. * * RETURNS * 0: Success * Atom: Failure */ ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ ) { return ATOM_DeleteAtom( ATOM_GlobalTable, atom ); } /*********************************************************************** * GlobalFindAtom16 (USER.270) */ ATOM WINAPI GlobalFindAtom16( SEGPTR str ) { if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ return ATOM_FindAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) ); } /*********************************************************************** * GlobalFindAtom32A (KERNEL32.318) * Searches the atom table for the string and returns the atom * associated with it. * * RETURNS * Atom: Success * 0: Failure */ ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ ) { if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ return ATOM_FindAtom( ATOM_GlobalTable, str ); } /*********************************************************************** * GlobalFindAtom32W (KERNEL32.319) * See GlobalFindAtom32A */ ATOM WINAPI GlobalFindAtomW( LPCWSTR str ) { char buffer[MAX_ATOM_LEN+1]; if (!HIWORD(str)) return (ATOM)LOWORD(str); /* Integer atom */ lstrcpynWtoA( buffer, str, sizeof(buffer) ); return ATOM_FindAtom( ATOM_GlobalTable, buffer ); } /*********************************************************************** * GlobalGetAtomName16 (USER.271) */ UINT16 WINAPI GlobalGetAtomName16( ATOM atom, LPSTR buffer, INT16 count ) { return (UINT16)ATOM_GetAtomName( ATOM_GlobalTable, atom, buffer, count ); } /*********************************************************************** * GlobalGetAtomName32A (KERNEL32.323) * Retrieves a copy of the string associated with an atom. * * RETURNS * Length of string in characters: Success * 0: Failure */ UINT WINAPI GlobalGetAtomNameA( ATOM atom, /* [in] Atom identifier */ LPSTR buffer, /* [out] Pointer to buffer for atom string */ INT count /* [in] Size of buffer */ ) { return ATOM_GetAtomName( ATOM_GlobalTable, atom, buffer, count ); } /*********************************************************************** * GlobalGetAtomName32W (KERNEL32.324) * See GlobalGetAtomName32A */ UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count ) { char tmp[MAX_ATOM_LEN+1]; ATOM_GetAtomName( ATOM_GlobalTable, atom, tmp, sizeof(tmp) ); lstrcpynAtoW( buffer, tmp, count ); return lstrlenW( buffer ); }