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
21 #include "wine/winbase16.h"
22 #include "wine/unicode.h"
25 #include "stackframe.h"
26 #include "debugtools.h"
29 DEFAULT_DEBUG_CHANNEL(atom);
31 #define DEFAULT_ATOMTABLE_SIZE 37
32 #define MIN_STR_ATOM 0xc000
33 #define MAX_ATOM_LEN 255
35 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2)
36 #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
52 static WORD ATOM_UserDS = 0; /* USER data segment */
54 /***********************************************************************
57 * Global table initialisation.
59 BOOL ATOM_Init( WORD globalTableSel )
61 ATOM_UserDS = globalTableSel;
66 /***********************************************************************
69 * Return a pointer to the atom table of a given segment, creating
73 * Pointer to table: Success
76 static ATOMTABLE *ATOM_GetTable( BOOL create /* [in] Create */ )
78 INSTANCEDATA *ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
81 ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
82 if (table->size) return table;
84 if (!create) return NULL;
85 if (!InitAtomTable16( 0 )) return NULL;
86 /* Reload ptr in case it moved in linear memory */
87 ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
88 return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
92 /***********************************************************************
95 * The hash value for the input string
97 static WORD ATOM_Hash(
98 WORD entries, /* [in] Total number of entries */
99 LPCSTR str, /* [in] Pointer to string to hash */
100 WORD len /* [in] Length of string */
104 TRACE("%x, %s, %x\n", entries, str, len);
106 for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
107 return hash % entries;
111 /***********************************************************************
114 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
117 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
120 if (*atomstr++ != '#') return FALSE;
121 while (*atomstr >= '0' && *atomstr <= '9')
123 atom = atom * 10 + *atomstr - '0';
126 if (*atomstr) return FALSE;
128 if (!atom || (atom >= MIN_STR_ATOM))
130 SetLastError( ERROR_INVALID_PARAMETER );
138 /***********************************************************************
141 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
144 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
147 if (*atomstr++ != '#') return FALSE;
148 while (*atomstr >= '0' && *atomstr <= '9')
150 atom = atom * 10 + *atomstr - '0';
153 if (*atomstr) return FALSE;
155 if (!atom || (atom >= MIN_STR_ATOM))
157 SetLastError( ERROR_INVALID_PARAMETER );
165 /***********************************************************************
168 * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
170 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
172 return MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
176 /***********************************************************************
177 * InitAtomTable16 (KERNEL.68)
179 WORD WINAPI InitAtomTable16( WORD entries )
185 /* We consider the first table to be initialized as the global table.
186 * This works, as USER (both built-in and native) is the first one to
192 ATOM_UserDS = CURRENT_DS;
193 /* return dummy local handle */
194 return LocalAlloc16( LMEM_FIXED, 1 );
197 /* Allocate the table */
199 if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */
200 handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
201 if (!handle) return 0;
202 table = MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
203 table->size = entries;
204 for (i = 0; i < entries; i++) table->entries[i] = 0;
206 /* Store a pointer to the table in the instance data */
208 ((INSTANCEDATA *)MapSL( MAKESEGPTR( CURRENT_DS, 0 )))->atomtable = handle;
212 /***********************************************************************
213 * GetAtomHandle (KERNEL.73)
215 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
217 if (atom < MIN_STR_ATOM) return 0;
218 return ATOMTOHANDLE( atom );
222 /***********************************************************************
223 * AddAtom16 (KERNEL.70)
225 * Windows DWORD aligns the atom entry size.
226 * The remaining unused string space created by the alignment
227 * gets padded with '\0's in a certain way to ensure
228 * that at least one trailing '\0' remains.
234 ATOM WINAPI AddAtom16( LPCSTR str )
236 char buffer[MAX_ATOM_LEN+1];
239 ATOMENTRY * entryPtr;
244 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
246 TRACE("%s\n",debugstr_a(buffer));
248 /* Make a copy of the string to be sure it doesn't move in linear memory. */
249 lstrcpynA( buffer, str, sizeof(buffer) );
251 len = strlen( buffer );
252 if (!(table = ATOM_GetTable( TRUE ))) return 0;
253 if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
255 hash = ATOM_Hash( table->size, buffer, len );
256 entry = table->entries[hash];
259 entryPtr = ATOM_MakePtr( entry );
260 if ((entryPtr->length == len) &&
261 (!strncasecmp( entryPtr->str, buffer, len )))
263 entryPtr->refCount++;
264 TRACE("-- existing 0x%x\n", entry);
265 return HANDLETOATOM( entry );
267 entry = entryPtr->next;
270 ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
271 entry = LocalAlloc16( LMEM_FIXED, ae_len );
272 if (!entry) return 0;
273 /* Reload the table ptr in case it moved in linear memory */
274 table = ATOM_GetTable( FALSE );
275 entryPtr = ATOM_MakePtr( entry );
276 entryPtr->next = table->entries[hash];
277 entryPtr->refCount = 1;
278 entryPtr->length = len;
279 /* Some applications _need_ the '\0' padding provided by this strncpy */
280 strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
281 entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
282 table->entries[hash] = entry;
283 TRACE("-- new 0x%x\n", entry);
284 return HANDLETOATOM( entry );
288 /***********************************************************************
289 * DeleteAtom16 (KERNEL.71)
291 ATOM WINAPI DeleteAtom16( ATOM atom )
293 ATOMENTRY * entryPtr;
295 HANDLE16 entry, *prevEntry;
298 if (atom < MIN_STR_ATOM) return 0; /* Integer atom */
299 if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
301 TRACE("0x%x\n",atom);
303 if (!(table = ATOM_GetTable( FALSE ))) return 0;
304 entry = ATOMTOHANDLE( atom );
305 entryPtr = ATOM_MakePtr( entry );
307 /* Find previous atom */
308 hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
309 prevEntry = &table->entries[hash];
310 while (*prevEntry && *prevEntry != entry)
312 ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
313 prevEntry = &prevEntryPtr->next;
315 if (!*prevEntry) return atom;
318 if (--entryPtr->refCount == 0)
320 *prevEntry = entryPtr->next;
321 LocalFree16( entry );
327 /***********************************************************************
328 * FindAtom16 (KERNEL.69)
330 ATOM WINAPI FindAtom16( LPCSTR str )
337 if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
339 TRACE("%s\n",debugres_a(str));
341 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
342 if ((len = strlen( str )) > 255) len = 255;
343 if (!(table = ATOM_GetTable( FALSE ))) return 0;
344 hash = ATOM_Hash( table->size, str, len );
345 entry = table->entries[hash];
348 ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
349 if ((entryPtr->length == len) &&
350 (!strncasecmp( entryPtr->str, str, len )))
352 TRACE("-- found %x\n", entry);
353 return HANDLETOATOM( entry );
355 entry = entryPtr->next;
357 TRACE("-- not found\n");
362 /***********************************************************************
363 * GetAtomName16 (KERNEL.72)
365 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
368 ATOMENTRY * entryPtr;
374 if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
378 if (!count) return 0;
379 if (atom < MIN_STR_ATOM)
381 sprintf( text, "#%d", atom );
387 if (!(table = ATOM_GetTable( FALSE ))) return 0;
388 entry = ATOMTOHANDLE( atom );
389 entryPtr = ATOM_MakePtr( entry );
390 len = entryPtr->length;
391 strPtr = entryPtr->str;
393 if (len >= count) len = count-1;
394 memcpy( buffer, strPtr, len );
399 /***********************************************************************
400 * InitAtomTable (KERNEL32.471)
402 BOOL WINAPI InitAtomTable( DWORD entries )
405 SERVER_START_REQ( init_atom_table )
407 req->entries = entries;
408 ret = !SERVER_CALL_ERR();
415 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
418 if (!ATOM_IsIntAtomA( str, &atom ))
420 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
421 if (len > MAX_ATOM_LEN)
423 SetLastError( ERROR_INVALID_PARAMETER );
426 SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
428 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
430 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
434 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
439 /***********************************************************************
440 * GlobalAddAtomA (USER.268) (KERNEL32.313)
442 * Adds a character string to the global atom table and returns a unique
443 * value identifying the string.
449 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
451 return ATOM_AddAtomA( str, FALSE );
455 /***********************************************************************
456 * AddAtomA (KERNEL32.0)
457 * Adds a string to the atom table and returns the atom identifying the
464 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
466 return ATOM_AddAtomA( str, TRUE );
470 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
473 if (!ATOM_IsIntAtomW( str, &atom ))
475 DWORD len = strlenW(str);
476 if (len > MAX_ATOM_LEN)
478 SetLastError( ERROR_INVALID_PARAMETER );
481 SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
483 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
485 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
489 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
494 /***********************************************************************
495 * GlobalAddAtomW (KERNEL32.314)
497 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
499 return ATOM_AddAtomW( str, FALSE );
503 /***********************************************************************
504 * AddAtomW (KERNEL32.1)
506 ATOM WINAPI AddAtomW( LPCWSTR str )
508 return ATOM_AddAtomW( str, TRUE );
512 static ATOM ATOM_DeleteAtom( ATOM atom, BOOL local)
514 TRACE( "(%s) %x\n", local ? "local" : "global", atom );
515 if (atom < MIN_STR_ATOM) atom = 0;
518 SERVER_START_REQ( delete_atom )
520 req->atom = atom - MIN_STR_ATOM;
522 if (!SERVER_CALL_ERR()) atom = 0;
530 /***********************************************************************
531 * GlobalDeleteAtom (USER.269) (KERNEL32.317)
532 * Decrements the reference count of a string atom. If the count is
533 * zero, the string associated with the atom is removed from the table.
539 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
541 return ATOM_DeleteAtom( atom, FALSE);
545 /***********************************************************************
546 * DeleteAtom (KERNEL32.69)
547 * Decrements the reference count of a string atom. If count becomes
548 * zero, the string associated with the atom is removed from the table.
554 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
556 return ATOM_DeleteAtom( atom, TRUE );
560 static ATOM ATOM_FindAtomA( LPCSTR str, BOOL local )
563 if (!ATOM_IsIntAtomA( str, &atom ))
565 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
566 if (len > MAX_ATOM_LEN)
568 SetLastError( ERROR_INVALID_PARAMETER );
571 SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
573 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
575 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
579 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
584 /***********************************************************************
585 * GlobalFindAtomA (USER.270) (KERNEL32.318)
587 * Searches the atom table for the string and returns the atom
588 * associated with it.
594 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
596 return ATOM_FindAtomA( str, FALSE );
599 /***********************************************************************
600 * FindAtomA (KERNEL32.117)
601 * Searches the local atom table for the string and returns the atom
602 * associated with that string.
608 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
610 return ATOM_FindAtomA( str, TRUE );
614 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
617 if (!ATOM_IsIntAtomW( str, &atom ))
619 DWORD len = strlenW(str);
620 if (len > MAX_ATOM_LEN)
622 SetLastError( ERROR_INVALID_PARAMETER );
625 SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
627 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
629 if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
633 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
638 /***********************************************************************
639 * GlobalFindAtomW (KERNEL32.319)
641 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
643 return ATOM_FindAtomW( str, FALSE );
647 /***********************************************************************
648 * FindAtomW (KERNEL32.118)
650 ATOM WINAPI FindAtomW( LPCWSTR str )
652 return ATOM_FindAtomW( str, TRUE );
656 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
662 SetLastError( ERROR_MORE_DATA );
665 if (atom < MIN_STR_ATOM)
670 SetLastError( ERROR_INVALID_PARAMETER );
673 len = sprintf( name, "#%d", atom );
674 lstrcpynA( buffer, name, count );
679 SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
681 req->atom = atom - MIN_STR_ATOM;
683 if (!SERVER_CALL_ERR())
685 len = WideCharToMultiByte( CP_ACP, 0, server_data_ptr(req),
686 server_data_size(req) / sizeof(WCHAR),
687 buffer, count - 1, NULL, NULL );
688 if (!len) len = count; /* overflow */
689 else buffer[len] = 0;
695 if (len && count <= len)
697 SetLastError( ERROR_MORE_DATA );
701 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
706 /***********************************************************************
707 * GlobalGetAtomNameA (USER.271) (KERNEL32.323)
709 * Retrieves a copy of the string associated with an atom.
712 * Length of string in characters: Success
715 UINT WINAPI GlobalGetAtomNameA(
716 ATOM atom, /* [in] Atom identifier */
717 LPSTR buffer, /* [out] Pointer to buffer for atom string */
718 INT count ) /* [in] Size of buffer */
720 return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
724 /***********************************************************************
725 * GetAtomNameA (KERNEL32.149)
726 * Retrieves a copy of the string associated with the atom.
729 * Length of string: Success
732 UINT WINAPI GetAtomNameA(
733 ATOM atom, /* [in] Atom */
734 LPSTR buffer, /* [out] Pointer to string for atom string */
735 INT count) /* [in] Size of buffer */
737 return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
741 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
747 SetLastError( ERROR_MORE_DATA );
750 if (atom < MIN_STR_ATOM)
755 SetLastError( ERROR_INVALID_PARAMETER );
758 sprintf( name, "#%d", atom );
759 len = MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, count );
760 if (!len) buffer[count-1] = 0; /* overflow */
765 SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
767 req->atom = atom - MIN_STR_ATOM;
769 if (!SERVER_CALL_ERR())
771 len = server_data_size(req) / sizeof(WCHAR);
772 if (count > len) count = len + 1;
773 memcpy( buffer, server_data_ptr(req), (count-1) * sizeof(WCHAR) );
782 SetLastError( ERROR_MORE_DATA );
785 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
790 /***********************************************************************
791 * GlobalGetAtomNameW (KERNEL32.324)
793 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
795 return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
799 /***********************************************************************
800 * GetAtomNameW (KERNEL32.150)
802 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
804 return ATOM_GetAtomNameW( atom, buffer, count, TRUE );