4 * Copyright 1993, 1994, 1995 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Warning: The code assumes that LocalAlloc() returns a block aligned
23 * on a 4-bytes boundary (because of the shifting done in
24 * HANDLETOATOM). If this is not the case, the allocation code will
39 #include "wine/server.h"
40 #include "wine/unicode.h"
41 #include "wine/winbase16.h"
44 #include "stackframe.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(atom);
50 #define DEFAULT_ATOMTABLE_SIZE 37
51 #define MAX_ATOM_LEN 255
53 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2)
54 #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
70 static WORD ATOM_UserDS = 0; /* USER data segment */
72 /***********************************************************************
75 * Global table initialisation.
77 BOOL ATOM_Init( WORD globalTableSel )
79 ATOM_UserDS = globalTableSel;
84 /***********************************************************************
87 * Return a pointer to the atom table of a given segment, creating
91 * Pointer to table: Success
94 static ATOMTABLE *ATOM_GetTable( BOOL create /* [in] Create */ )
96 INSTANCEDATA *ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
99 ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
100 if (table->size) return table;
102 if (!create) return NULL;
103 if (!InitAtomTable16( 0 )) return NULL;
104 /* Reload ptr in case it moved in linear memory */
105 ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
106 return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
110 /***********************************************************************
113 * The hash value for the input string
115 static WORD ATOM_Hash(
116 WORD entries, /* [in] Total number of entries */
117 LPCSTR str, /* [in] Pointer to string to hash */
118 WORD len /* [in] Length of string */
122 TRACE("%x, %s, %x\n", entries, str, len);
124 for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
125 return hash % entries;
129 /***********************************************************************
132 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
135 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
138 if (*atomstr++ != '#') return FALSE;
139 while (*atomstr >= '0' && *atomstr <= '9')
141 atom = atom * 10 + *atomstr - '0';
144 if (*atomstr) return FALSE;
146 if (!atom || (atom >= MAXINTATOM))
148 SetLastError( ERROR_INVALID_PARAMETER );
156 /***********************************************************************
159 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
162 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
165 if (*atomstr++ != '#') return FALSE;
166 while (*atomstr >= '0' && *atomstr <= '9')
168 atom = atom * 10 + *atomstr - '0';
171 if (*atomstr) return FALSE;
173 if (!atom || (atom >= MAXINTATOM))
175 SetLastError( ERROR_INVALID_PARAMETER );
183 /***********************************************************************
186 * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
188 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
190 return MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
194 /***********************************************************************
195 * InitAtomTable (KERNEL.68)
197 WORD WINAPI InitAtomTable16( WORD entries )
203 /* We consider the first table to be initialized as the global table.
204 * This works, as USER (both built-in and native) is the first one to
210 ATOM_UserDS = CURRENT_DS;
211 /* return dummy local handle */
212 return LocalAlloc16( LMEM_FIXED, 1 );
215 /* Allocate the table */
217 if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */
218 handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
219 if (!handle) return 0;
220 table = MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
221 table->size = entries;
222 for (i = 0; i < entries; i++) table->entries[i] = 0;
224 /* Store a pointer to the table in the instance data */
226 ((INSTANCEDATA *)MapSL( MAKESEGPTR( CURRENT_DS, 0 )))->atomtable = handle;
230 /***********************************************************************
231 * GetAtomHandle (KERNEL.73)
233 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
235 if (atom < MAXINTATOM) return 0;
236 return ATOMTOHANDLE( atom );
240 /***********************************************************************
241 * AddAtom (KERNEL.70)
243 * Windows DWORD aligns the atom entry size.
244 * The remaining unused string space created by the alignment
245 * gets padded with '\0's in a certain way to ensure
246 * that at least one trailing '\0' remains.
252 ATOM WINAPI AddAtom16( LPCSTR str )
254 char buffer[MAX_ATOM_LEN+1];
257 ATOMENTRY * entryPtr;
262 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
264 TRACE("%s\n",debugstr_a(buffer));
266 /* Make a copy of the string to be sure it doesn't move in linear memory. */
267 lstrcpynA( buffer, str, sizeof(buffer) );
269 len = strlen( buffer );
270 if (!(table = ATOM_GetTable( TRUE ))) return 0;
271 if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
273 hash = ATOM_Hash( table->size, buffer, len );
274 entry = table->entries[hash];
277 entryPtr = ATOM_MakePtr( entry );
278 if ((entryPtr->length == len) &&
279 (!strncasecmp( entryPtr->str, buffer, len )))
281 entryPtr->refCount++;
282 TRACE("-- existing 0x%x\n", entry);
283 return HANDLETOATOM( entry );
285 entry = entryPtr->next;
288 ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
289 entry = LocalAlloc16( LMEM_FIXED, ae_len );
290 if (!entry) return 0;
291 /* Reload the table ptr in case it moved in linear memory */
292 table = ATOM_GetTable( FALSE );
293 entryPtr = ATOM_MakePtr( entry );
294 entryPtr->next = table->entries[hash];
295 entryPtr->refCount = 1;
296 entryPtr->length = len;
297 /* Some applications _need_ the '\0' padding provided by this strncpy */
298 strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
299 entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
300 table->entries[hash] = entry;
301 TRACE("-- new 0x%x\n", entry);
302 return HANDLETOATOM( entry );
306 /***********************************************************************
307 * DeleteAtom (KERNEL.71)
309 ATOM WINAPI DeleteAtom16( ATOM atom )
311 ATOMENTRY * entryPtr;
313 HANDLE16 entry, *prevEntry;
316 if (atom < MAXINTATOM) return 0; /* Integer atom */
317 if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
319 TRACE("0x%x\n",atom);
321 if (!(table = ATOM_GetTable( FALSE ))) return 0;
322 entry = ATOMTOHANDLE( atom );
323 entryPtr = ATOM_MakePtr( entry );
325 /* Find previous atom */
326 hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
327 prevEntry = &table->entries[hash];
328 while (*prevEntry && *prevEntry != entry)
330 ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
331 prevEntry = &prevEntryPtr->next;
333 if (!*prevEntry) return atom;
336 if (--entryPtr->refCount == 0)
338 *prevEntry = entryPtr->next;
339 LocalFree16( entry );
345 /***********************************************************************
346 * FindAtom (KERNEL.69)
348 ATOM WINAPI FindAtom16( LPCSTR str )
355 if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
357 TRACE("%s\n",debugstr_a(str));
359 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
360 if ((len = strlen( str )) > 255) len = 255;
361 if (!(table = ATOM_GetTable( FALSE ))) return 0;
362 hash = ATOM_Hash( table->size, str, len );
363 entry = table->entries[hash];
366 ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
367 if ((entryPtr->length == len) &&
368 (!strncasecmp( entryPtr->str, str, len )))
370 TRACE("-- found %x\n", entry);
371 return HANDLETOATOM( entry );
373 entry = entryPtr->next;
375 TRACE("-- not found\n");
380 /***********************************************************************
381 * GetAtomName (KERNEL.72)
383 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
386 ATOMENTRY * entryPtr;
392 if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
396 if (!count) return 0;
397 if (atom < MAXINTATOM)
399 sprintf( text, "#%d", atom );
405 if (!(table = ATOM_GetTable( FALSE ))) return 0;
406 entry = ATOMTOHANDLE( atom );
407 entryPtr = ATOM_MakePtr( entry );
408 len = entryPtr->length;
409 strPtr = entryPtr->str;
411 if (len >= count) len = count-1;
412 memcpy( buffer, strPtr, len );
417 /***********************************************************************
418 * InitAtomTable (KERNEL32.@)
420 BOOL WINAPI InitAtomTable( DWORD entries )
423 SERVER_START_REQ( init_atom_table )
425 req->entries = entries;
426 ret = !wine_server_call_err( req );
433 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
436 if (!ATOM_IsIntAtomA( str, &atom ))
438 WCHAR buffer[MAX_ATOM_LEN];
440 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), buffer, MAX_ATOM_LEN );
443 SetLastError( ERROR_INVALID_PARAMETER );
446 SERVER_START_REQ( add_atom )
448 wine_server_add_data( req, buffer, len * sizeof(WCHAR) );
450 if (!wine_server_call_err(req)) atom = reply->atom;
454 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugstr_a(str), atom );
459 /***********************************************************************
460 * GlobalAddAtomA (KERNEL32.@)
462 * Adds a character string to the global atom table and returns a unique
463 * value identifying the string.
469 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
471 return ATOM_AddAtomA( str, FALSE );
475 /***********************************************************************
476 * AddAtomA (KERNEL32.@)
477 * Adds a string to the atom table and returns the atom identifying the
484 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
486 return ATOM_AddAtomA( str, TRUE );
490 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
493 if (!ATOM_IsIntAtomW( str, &atom ))
495 DWORD len = strlenW(str);
496 if (len > MAX_ATOM_LEN)
498 SetLastError( ERROR_INVALID_PARAMETER );
501 SERVER_START_REQ( add_atom )
504 wine_server_add_data( req, str, len * sizeof(WCHAR) );
505 if (!wine_server_call_err(req)) atom = reply->atom;
509 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugstr_w(str), atom );
514 /***********************************************************************
515 * GlobalAddAtomW (KERNEL32.@)
517 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
519 return ATOM_AddAtomW( str, FALSE );
523 /***********************************************************************
524 * AddAtomW (KERNEL32.@)
526 ATOM WINAPI AddAtomW( LPCWSTR str )
528 return ATOM_AddAtomW( str, TRUE );
532 static ATOM ATOM_DeleteAtom( ATOM atom, BOOL local)
534 TRACE( "(%s) %x\n", local ? "local" : "global", atom );
535 if (atom < MAXINTATOM) atom = 0;
538 SERVER_START_REQ( delete_atom )
542 if (!wine_server_call_err( req )) atom = 0;
550 /***********************************************************************
551 * GlobalDeleteAtom (KERNEL32.@)
552 * Decrements the reference count of a string atom. If the count is
553 * zero, the string associated with the atom is removed from the table.
559 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
561 return ATOM_DeleteAtom( atom, FALSE);
565 /***********************************************************************
566 * DeleteAtom (KERNEL32.@)
567 * Decrements the reference count of a string atom. If count becomes
568 * zero, the string associated with the atom is removed from the table.
574 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
576 return ATOM_DeleteAtom( atom, TRUE );
580 static ATOM ATOM_FindAtomA( LPCSTR str, BOOL local )
583 if (!ATOM_IsIntAtomA( str, &atom ))
585 WCHAR buffer[MAX_ATOM_LEN];
587 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), buffer, MAX_ATOM_LEN );
590 SetLastError( ERROR_INVALID_PARAMETER );
593 SERVER_START_REQ( find_atom )
596 wine_server_add_data( req, buffer, len * sizeof(WCHAR) );
597 if (!wine_server_call_err(req)) atom = reply->atom;
601 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugstr_a(str), atom );
606 /***********************************************************************
607 * GlobalFindAtomA (KERNEL32.@)
609 * Searches the atom table for the string and returns the atom
610 * associated with it.
616 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
618 return ATOM_FindAtomA( str, FALSE );
621 /***********************************************************************
622 * FindAtomA (KERNEL32.@)
623 * Searches the local atom table for the string and returns the atom
624 * associated with that string.
630 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
632 return ATOM_FindAtomA( str, TRUE );
636 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
639 if (!ATOM_IsIntAtomW( str, &atom ))
641 DWORD len = strlenW(str);
642 if (len > MAX_ATOM_LEN)
644 SetLastError( ERROR_INVALID_PARAMETER );
647 SERVER_START_REQ( find_atom )
649 wine_server_add_data( req, str, len * sizeof(WCHAR) );
651 if (!wine_server_call_err( req )) atom = reply->atom;
655 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugstr_w(str), atom );
660 /***********************************************************************
661 * GlobalFindAtomW (KERNEL32.@)
663 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
665 return ATOM_FindAtomW( str, FALSE );
669 /***********************************************************************
670 * FindAtomW (KERNEL32.@)
672 ATOM WINAPI FindAtomW( LPCWSTR str )
674 return ATOM_FindAtomW( str, TRUE );
678 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
684 SetLastError( ERROR_MORE_DATA );
687 if (atom < MAXINTATOM)
692 SetLastError( ERROR_INVALID_PARAMETER );
695 len = sprintf( name, "#%d", atom );
696 lstrcpynA( buffer, name, count );
700 WCHAR full_name[MAX_ATOM_LEN];
703 SERVER_START_REQ( get_atom_name )
707 wine_server_set_reply( req, full_name, sizeof(full_name) );
708 if (!wine_server_call_err( req ))
710 len = WideCharToMultiByte( CP_ACP, 0, full_name,
711 wine_server_reply_size(reply) / sizeof(WCHAR),
712 buffer, count - 1, NULL, NULL );
713 if (!len) len = count; /* overflow */
714 else buffer[len] = 0;
720 if (len && count <= len)
722 SetLastError( ERROR_MORE_DATA );
726 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
731 /***********************************************************************
732 * GlobalGetAtomNameA (KERNEL32.@)
734 * Retrieves a copy of the string associated with an atom.
737 * Length of string in characters: Success
740 UINT WINAPI GlobalGetAtomNameA(
741 ATOM atom, /* [in] Atom identifier */
742 LPSTR buffer, /* [out] Pointer to buffer for atom string */
743 INT count ) /* [in] Size of buffer */
745 return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
749 /***********************************************************************
750 * GetAtomNameA (KERNEL32.@)
751 * Retrieves a copy of the string associated with the atom.
754 * Length of string: Success
757 UINT WINAPI GetAtomNameA(
758 ATOM atom, /* [in] Atom */
759 LPSTR buffer, /* [out] Pointer to string for atom string */
760 INT count) /* [in] Size of buffer */
762 return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
766 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
772 SetLastError( ERROR_MORE_DATA );
775 if (atom < MAXINTATOM)
780 SetLastError( ERROR_INVALID_PARAMETER );
783 sprintf( name, "#%d", atom );
784 len = MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, count );
785 if (!len) buffer[count-1] = 0; /* overflow */
789 WCHAR full_name[MAX_ATOM_LEN];
792 SERVER_START_REQ( get_atom_name )
796 wine_server_set_reply( req, full_name, sizeof(full_name) );
797 if (!wine_server_call_err( req ))
799 len = wine_server_reply_size(reply) / sizeof(WCHAR);
800 if (count > len) count = len + 1;
801 memcpy( buffer, full_name, (count-1) * sizeof(WCHAR) );
810 SetLastError( ERROR_MORE_DATA );
813 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
818 /***********************************************************************
819 * GlobalGetAtomNameW (KERNEL32.@)
821 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
823 return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
827 /***********************************************************************
828 * GetAtomNameW (KERNEL32.@)
830 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
832 return ATOM_GetAtomNameW( atom, buffer, count, TRUE );