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 )
407 struct init_atom_table_request *req = server_alloc_req( sizeof(*req), 0 );
408 req->entries = entries;
409 ret = !server_call( REQ_INIT_ATOM_TABLE );
416 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
419 if (!ATOM_IsIntAtomA( str, &atom ))
421 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
422 if (len > MAX_ATOM_LEN)
424 SetLastError( ERROR_INVALID_PARAMETER );
429 struct add_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
430 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
432 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
436 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
441 /***********************************************************************
442 * GlobalAddAtomA (USER.268) (KERNEL32.313)
444 * Adds a character string to the global atom table and returns a unique
445 * value identifying the string.
451 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
453 return ATOM_AddAtomA( str, FALSE );
457 /***********************************************************************
458 * AddAtomA (KERNEL32.0)
459 * Adds a string to the atom table and returns the atom identifying the
466 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
468 return ATOM_AddAtomA( str, TRUE );
472 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
475 if (!ATOM_IsIntAtomW( str, &atom ))
477 DWORD len = strlenW(str);
478 if (len > MAX_ATOM_LEN)
480 SetLastError( ERROR_INVALID_PARAMETER );
485 struct add_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
486 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
488 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
492 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
497 /***********************************************************************
498 * GlobalAddAtomW (KERNEL32.314)
500 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
502 return ATOM_AddAtomW( str, FALSE );
506 /***********************************************************************
507 * AddAtomW (KERNEL32.1)
509 ATOM WINAPI AddAtomW( LPCWSTR str )
511 return ATOM_AddAtomW( str, TRUE );
515 static ATOM ATOM_DeleteAtom( ATOM atom, BOOL local)
517 TRACE( "(%s) %x\n", local ? "local" : "glbal", atom );
518 if (atom < MIN_STR_ATOM) atom = 0;
523 struct delete_atom_request *req = server_alloc_req( sizeof(*req), 0 );
524 req->atom = atom - MIN_STR_ATOM;
526 if (!server_call( REQ_DELETE_ATOM )) atom = 0;
534 /***********************************************************************
535 * GlobalDeleteAtom (USER.269) (KERNEL32.317)
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.69)
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 );
577 struct find_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
578 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
580 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
584 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
589 /***********************************************************************
590 * GlobalFindAtomA (USER.270) (KERNEL32.318)
592 * Searches the atom table for the string and returns the atom
593 * associated with it.
599 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
601 return ATOM_FindAtomA( str, FALSE );
604 /***********************************************************************
605 * FindAtomA (KERNEL32.117)
606 * Searches the local atom table for the string and returns the atom
607 * associated with that string.
613 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
615 return ATOM_FindAtomA( str, TRUE );
619 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
622 if (!ATOM_IsIntAtomW( str, &atom ))
624 DWORD len = strlenW(str);
625 if (len > MAX_ATOM_LEN)
627 SetLastError( ERROR_INVALID_PARAMETER );
632 struct find_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
633 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
635 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
639 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
644 /***********************************************************************
645 * GlobalFindAtomW (KERNEL32.319)
647 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
649 return ATOM_FindAtomW( str, FALSE );
653 /***********************************************************************
654 * FindAtomW (KERNEL32.118)
656 ATOM WINAPI FindAtomW( LPCWSTR str )
658 return ATOM_FindAtomW( str, TRUE );
662 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
668 SetLastError( ERROR_MORE_DATA );
671 if (atom < MIN_STR_ATOM)
676 SetLastError( ERROR_INVALID_PARAMETER );
679 len = sprintf( name, "#%d", atom );
680 lstrcpynA( buffer, name, count );
687 struct get_atom_name_request *req = server_alloc_req( sizeof(*req),
688 MAX_ATOM_LEN * sizeof(WCHAR) );
689 req->atom = atom - MIN_STR_ATOM;
691 if (!server_call( REQ_GET_ATOM_NAME ))
693 len = WideCharToMultiByte( CP_ACP, 0, server_data_ptr(req),
694 server_data_size(req) / sizeof(WCHAR),
695 buffer, count - 1, NULL, NULL );
696 if (!len) len = count; /* overflow */
697 else buffer[len] = 0;
703 if (len && count <= len)
705 SetLastError( ERROR_MORE_DATA );
709 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
714 /***********************************************************************
715 * GlobalGetAtomNameA (USER.271) (KERNEL32.323)
717 * Retrieves a copy of the string associated with an atom.
720 * Length of string in characters: Success
723 UINT WINAPI GlobalGetAtomNameA(
724 ATOM atom, /* [in] Atom identifier */
725 LPSTR buffer, /* [out] Pointer to buffer for atom string */
726 INT count ) /* [in] Size of buffer */
728 return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
732 /***********************************************************************
733 * GetAtomNameA (KERNEL32.149)
734 * Retrieves a copy of the string associated with the atom.
737 * Length of string: Success
740 UINT WINAPI GetAtomNameA(
741 ATOM atom, /* [in] Atom */
742 LPSTR buffer, /* [out] Pointer to string for atom string */
743 INT count) /* [in] Size of buffer */
745 return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
749 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
755 SetLastError( ERROR_MORE_DATA );
758 if (atom < MIN_STR_ATOM)
763 SetLastError( ERROR_INVALID_PARAMETER );
766 sprintf( name, "#%d", atom );
767 len = MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, count );
768 if (!len) buffer[count-1] = 0; /* overflow */
775 struct get_atom_name_request *req = server_alloc_req( sizeof(*req),
776 MAX_ATOM_LEN * sizeof(WCHAR) );
777 req->atom = atom - MIN_STR_ATOM;
779 if (!server_call( REQ_GET_ATOM_NAME ))
781 len = server_data_size(req) / sizeof(WCHAR);
782 if (count > len) count = len + 1;
783 memcpy( buffer, server_data_ptr(req), (count-1) * sizeof(WCHAR) );
792 SetLastError( ERROR_MORE_DATA );
795 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
800 /***********************************************************************
801 * GlobalGetAtomNameW (KERNEL32.324)
803 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
805 return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
809 /***********************************************************************
810 * GetAtomNameW (KERNEL32.150)
812 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
814 return ATOM_GetAtomNameW( atom, buffer, count, TRUE );