4 * Copyright 1993, 1994, 1995 Alexandre Julliard
8 * Warning: The code assumes that LocalAlloc() returns a block aligned
9 * on a 4-bytes boundary (because of the shifting done in
10 * HANDLETOATOM). If this is not the case, the allocation code will
24 #include "wine/server.h"
25 #include "wine/unicode.h"
26 #include "wine/winbase16.h"
29 #include "stackframe.h"
31 #include "debugtools.h"
33 DEFAULT_DEBUG_CHANNEL(atom);
35 #define DEFAULT_ATOMTABLE_SIZE 37
36 #define MIN_STR_ATOM 0xc000
37 #define MAX_ATOM_LEN 255
39 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2)
40 #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
56 static WORD ATOM_UserDS = 0; /* USER data segment */
58 /***********************************************************************
61 * Global table initialisation.
63 BOOL ATOM_Init( WORD globalTableSel )
65 ATOM_UserDS = globalTableSel;
70 /***********************************************************************
73 * Return a pointer to the atom table of a given segment, creating
77 * Pointer to table: Success
80 static ATOMTABLE *ATOM_GetTable( BOOL create /* [in] Create */ )
82 INSTANCEDATA *ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
85 ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
86 if (table->size) return table;
88 if (!create) return NULL;
89 if (!InitAtomTable16( 0 )) return NULL;
90 /* Reload ptr in case it moved in linear memory */
91 ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
92 return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
96 /***********************************************************************
99 * The hash value for the input string
101 static WORD ATOM_Hash(
102 WORD entries, /* [in] Total number of entries */
103 LPCSTR str, /* [in] Pointer to string to hash */
104 WORD len /* [in] Length of string */
108 TRACE("%x, %s, %x\n", entries, str, len);
110 for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
111 return hash % entries;
115 /***********************************************************************
118 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
121 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
124 if (*atomstr++ != '#') return FALSE;
125 while (*atomstr >= '0' && *atomstr <= '9')
127 atom = atom * 10 + *atomstr - '0';
130 if (*atomstr) return FALSE;
132 if (!atom || (atom >= MIN_STR_ATOM))
134 SetLastError( ERROR_INVALID_PARAMETER );
142 /***********************************************************************
145 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
148 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
151 if (*atomstr++ != '#') return FALSE;
152 while (*atomstr >= '0' && *atomstr <= '9')
154 atom = atom * 10 + *atomstr - '0';
157 if (*atomstr) return FALSE;
159 if (!atom || (atom >= MIN_STR_ATOM))
161 SetLastError( ERROR_INVALID_PARAMETER );
169 /***********************************************************************
172 * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
174 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
176 return MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
180 /***********************************************************************
181 * InitAtomTable (KERNEL.68)
183 WORD WINAPI InitAtomTable16( WORD entries )
189 /* We consider the first table to be initialized as the global table.
190 * This works, as USER (both built-in and native) is the first one to
196 ATOM_UserDS = CURRENT_DS;
197 /* return dummy local handle */
198 return LocalAlloc16( LMEM_FIXED, 1 );
201 /* Allocate the table */
203 if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */
204 handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
205 if (!handle) return 0;
206 table = MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
207 table->size = entries;
208 for (i = 0; i < entries; i++) table->entries[i] = 0;
210 /* Store a pointer to the table in the instance data */
212 ((INSTANCEDATA *)MapSL( MAKESEGPTR( CURRENT_DS, 0 )))->atomtable = handle;
216 /***********************************************************************
217 * GetAtomHandle (KERNEL.73)
219 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
221 if (atom < MIN_STR_ATOM) return 0;
222 return ATOMTOHANDLE( atom );
226 /***********************************************************************
227 * AddAtom (KERNEL.70)
229 * Windows DWORD aligns the atom entry size.
230 * The remaining unused string space created by the alignment
231 * gets padded with '\0's in a certain way to ensure
232 * that at least one trailing '\0' remains.
238 ATOM WINAPI AddAtom16( LPCSTR str )
240 char buffer[MAX_ATOM_LEN+1];
243 ATOMENTRY * entryPtr;
248 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
250 TRACE("%s\n",debugstr_a(buffer));
252 /* Make a copy of the string to be sure it doesn't move in linear memory. */
253 lstrcpynA( buffer, str, sizeof(buffer) );
255 len = strlen( buffer );
256 if (!(table = ATOM_GetTable( TRUE ))) return 0;
257 if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
259 hash = ATOM_Hash( table->size, buffer, len );
260 entry = table->entries[hash];
263 entryPtr = ATOM_MakePtr( entry );
264 if ((entryPtr->length == len) &&
265 (!strncasecmp( entryPtr->str, buffer, len )))
267 entryPtr->refCount++;
268 TRACE("-- existing 0x%x\n", entry);
269 return HANDLETOATOM( entry );
271 entry = entryPtr->next;
274 ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
275 entry = LocalAlloc16( LMEM_FIXED, ae_len );
276 if (!entry) return 0;
277 /* Reload the table ptr in case it moved in linear memory */
278 table = ATOM_GetTable( FALSE );
279 entryPtr = ATOM_MakePtr( entry );
280 entryPtr->next = table->entries[hash];
281 entryPtr->refCount = 1;
282 entryPtr->length = len;
283 /* Some applications _need_ the '\0' padding provided by this strncpy */
284 strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
285 entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
286 table->entries[hash] = entry;
287 TRACE("-- new 0x%x\n", entry);
288 return HANDLETOATOM( entry );
292 /***********************************************************************
293 * DeleteAtom (KERNEL.71)
295 ATOM WINAPI DeleteAtom16( ATOM atom )
297 ATOMENTRY * entryPtr;
299 HANDLE16 entry, *prevEntry;
302 if (atom < MIN_STR_ATOM) return 0; /* Integer atom */
303 if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
305 TRACE("0x%x\n",atom);
307 if (!(table = ATOM_GetTable( FALSE ))) return 0;
308 entry = ATOMTOHANDLE( atom );
309 entryPtr = ATOM_MakePtr( entry );
311 /* Find previous atom */
312 hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
313 prevEntry = &table->entries[hash];
314 while (*prevEntry && *prevEntry != entry)
316 ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
317 prevEntry = &prevEntryPtr->next;
319 if (!*prevEntry) return atom;
322 if (--entryPtr->refCount == 0)
324 *prevEntry = entryPtr->next;
325 LocalFree16( entry );
331 /***********************************************************************
332 * FindAtom (KERNEL.69)
334 ATOM WINAPI FindAtom16( LPCSTR str )
341 if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
343 TRACE("%s\n",debugres_a(str));
345 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
346 if ((len = strlen( str )) > 255) len = 255;
347 if (!(table = ATOM_GetTable( FALSE ))) return 0;
348 hash = ATOM_Hash( table->size, str, len );
349 entry = table->entries[hash];
352 ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
353 if ((entryPtr->length == len) &&
354 (!strncasecmp( entryPtr->str, str, len )))
356 TRACE("-- found %x\n", entry);
357 return HANDLETOATOM( entry );
359 entry = entryPtr->next;
361 TRACE("-- not found\n");
366 /***********************************************************************
367 * GetAtomName (KERNEL.72)
369 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
372 ATOMENTRY * entryPtr;
378 if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
382 if (!count) return 0;
383 if (atom < MIN_STR_ATOM)
385 sprintf( text, "#%d", atom );
391 if (!(table = ATOM_GetTable( FALSE ))) return 0;
392 entry = ATOMTOHANDLE( atom );
393 entryPtr = ATOM_MakePtr( entry );
394 len = entryPtr->length;
395 strPtr = entryPtr->str;
397 if (len >= count) len = count-1;
398 memcpy( buffer, strPtr, len );
403 /***********************************************************************
404 * InitAtomTable (KERNEL32.@)
406 BOOL WINAPI InitAtomTable( DWORD entries )
409 SERVER_START_REQ( init_atom_table )
411 req->entries = entries;
412 ret = !SERVER_CALL_ERR();
419 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
422 if (!ATOM_IsIntAtomA( str, &atom ))
424 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
425 if (len > MAX_ATOM_LEN)
427 SetLastError( ERROR_INVALID_PARAMETER );
430 SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
432 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
434 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
438 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
443 /***********************************************************************
444 * GlobalAddAtomA (KERNEL32.@)
446 * Adds a character string to the global atom table and returns a unique
447 * value identifying the string.
453 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
455 return ATOM_AddAtomA( str, FALSE );
459 /***********************************************************************
460 * AddAtomA (KERNEL32.@)
461 * Adds a string to the atom table and returns the atom identifying the
468 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
470 return ATOM_AddAtomA( str, TRUE );
474 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
477 if (!ATOM_IsIntAtomW( str, &atom ))
479 DWORD len = strlenW(str);
480 if (len > MAX_ATOM_LEN)
482 SetLastError( ERROR_INVALID_PARAMETER );
485 SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
487 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
489 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
493 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
498 /***********************************************************************
499 * GlobalAddAtomW (KERNEL32.@)
501 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
503 return ATOM_AddAtomW( str, FALSE );
507 /***********************************************************************
508 * AddAtomW (KERNEL32.@)
510 ATOM WINAPI AddAtomW( LPCWSTR str )
512 return ATOM_AddAtomW( str, TRUE );
516 static ATOM ATOM_DeleteAtom( ATOM atom, BOOL local)
518 TRACE( "(%s) %x\n", local ? "local" : "global", atom );
519 if (atom < MIN_STR_ATOM) atom = 0;
522 SERVER_START_REQ( delete_atom )
524 req->atom = atom - MIN_STR_ATOM;
526 if (!SERVER_CALL_ERR()) atom = 0;
534 /***********************************************************************
535 * GlobalDeleteAtom (KERNEL32.@)
536 * Decrements the reference count of a string atom. If the count is
537 * zero, the string associated with the atom is removed from the table.
543 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
545 return ATOM_DeleteAtom( atom, FALSE);
549 /***********************************************************************
550 * DeleteAtom (KERNEL32.@)
551 * Decrements the reference count of a string atom. If count becomes
552 * zero, the string associated with the atom is removed from the table.
558 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
560 return ATOM_DeleteAtom( atom, TRUE );
564 static ATOM ATOM_FindAtomA( LPCSTR str, BOOL local )
567 if (!ATOM_IsIntAtomA( str, &atom ))
569 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
570 if (len > MAX_ATOM_LEN)
572 SetLastError( ERROR_INVALID_PARAMETER );
575 SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
577 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
579 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
583 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
588 /***********************************************************************
589 * GlobalFindAtomA (KERNEL32.@)
591 * Searches the atom table for the string and returns the atom
592 * associated with it.
598 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
600 return ATOM_FindAtomA( str, FALSE );
603 /***********************************************************************
604 * FindAtomA (KERNEL32.@)
605 * Searches the local atom table for the string and returns the atom
606 * associated with that string.
612 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
614 return ATOM_FindAtomA( str, TRUE );
618 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
621 if (!ATOM_IsIntAtomW( str, &atom ))
623 DWORD len = strlenW(str);
624 if (len > MAX_ATOM_LEN)
626 SetLastError( ERROR_INVALID_PARAMETER );
629 SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
631 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
633 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
637 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
642 /***********************************************************************
643 * GlobalFindAtomW (KERNEL32.@)
645 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
647 return ATOM_FindAtomW( str, FALSE );
651 /***********************************************************************
652 * FindAtomW (KERNEL32.@)
654 ATOM WINAPI FindAtomW( LPCWSTR str )
656 return ATOM_FindAtomW( str, TRUE );
660 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
666 SetLastError( ERROR_MORE_DATA );
669 if (atom < MIN_STR_ATOM)
674 SetLastError( ERROR_INVALID_PARAMETER );
677 len = sprintf( name, "#%d", atom );
678 lstrcpynA( buffer, name, count );
683 SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
685 req->atom = atom - MIN_STR_ATOM;
687 if (!SERVER_CALL_ERR())
689 len = WideCharToMultiByte( CP_ACP, 0, server_data_ptr(req),
690 server_data_size(req) / sizeof(WCHAR),
691 buffer, count - 1, NULL, NULL );
692 if (!len) len = count; /* overflow */
693 else buffer[len] = 0;
699 if (len && count <= len)
701 SetLastError( ERROR_MORE_DATA );
705 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
710 /***********************************************************************
711 * GlobalGetAtomNameA (KERNEL32.@)
713 * Retrieves a copy of the string associated with an atom.
716 * Length of string in characters: Success
719 UINT WINAPI GlobalGetAtomNameA(
720 ATOM atom, /* [in] Atom identifier */
721 LPSTR buffer, /* [out] Pointer to buffer for atom string */
722 INT count ) /* [in] Size of buffer */
724 return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
728 /***********************************************************************
729 * GetAtomNameA (KERNEL32.@)
730 * Retrieves a copy of the string associated with the atom.
733 * Length of string: Success
736 UINT WINAPI GetAtomNameA(
737 ATOM atom, /* [in] Atom */
738 LPSTR buffer, /* [out] Pointer to string for atom string */
739 INT count) /* [in] Size of buffer */
741 return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
745 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
751 SetLastError( ERROR_MORE_DATA );
754 if (atom < MIN_STR_ATOM)
759 SetLastError( ERROR_INVALID_PARAMETER );
762 sprintf( name, "#%d", atom );
763 len = MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, count );
764 if (!len) buffer[count-1] = 0; /* overflow */
769 SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
771 req->atom = atom - MIN_STR_ATOM;
773 if (!SERVER_CALL_ERR())
775 len = server_data_size(req) / sizeof(WCHAR);
776 if (count > len) count = len + 1;
777 memcpy( buffer, server_data_ptr(req), (count-1) * sizeof(WCHAR) );
786 SetLastError( ERROR_MORE_DATA );
789 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
794 /***********************************************************************
795 * GlobalGetAtomNameW (KERNEL32.@)
797 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
799 return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
803 /***********************************************************************
804 * GetAtomNameW (KERNEL32.@)
806 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
808 return ATOM_GetAtomNameW( atom, buffer, count, TRUE );