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"
26 #include "stackframe.h"
27 #include "debugtools.h"
30 DEFAULT_DEBUG_CHANNEL(atom);
32 #define DEFAULT_ATOMTABLE_SIZE 37
33 #define MIN_STR_ATOM 0xc000
34 #define MAX_ATOM_LEN 255
36 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2)
37 #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
53 static WORD ATOM_UserDS = 0; /* USER data segment */
55 /***********************************************************************
58 * Global table initialisation.
60 BOOL ATOM_Init( WORD globalTableSel )
62 ATOM_UserDS = globalTableSel;
67 /***********************************************************************
70 * Return a pointer to the atom table of a given segment, creating
74 * Pointer to table: Success
77 static ATOMTABLE *ATOM_GetTable( BOOL create /* [in] Create */ )
79 INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
82 ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
83 if (table->size) return table;
85 if (!create) return NULL;
86 if (!InitAtomTable16( 0 )) return NULL;
87 /* Reload ptr in case it moved in linear memory */
88 ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
89 return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
93 /***********************************************************************
96 * The hash value for the input string
98 static WORD ATOM_Hash(
99 WORD entries, /* [in] Total number of entries */
100 LPCSTR str, /* [in] Pointer to string to hash */
101 WORD len /* [in] Length of string */
105 TRACE("%x, %s, %x\n", entries, str, len);
107 for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
108 return hash % entries;
112 /***********************************************************************
115 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
118 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
121 if (*atomstr++ != '#') return FALSE;
122 while (*atomstr >= '0' && *atomstr <= '9')
124 atom = atom * 10 + *atomstr - '0';
127 if (*atomstr) return FALSE;
129 if (!atom || (atom >= MIN_STR_ATOM))
131 SetLastError( ERROR_INVALID_PARAMETER );
139 /***********************************************************************
142 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
145 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
148 if (*atomstr++ != '#') return FALSE;
149 while (*atomstr >= '0' && *atomstr <= '9')
151 atom = atom * 10 + *atomstr - '0';
154 if (*atomstr) return FALSE;
156 if (!atom || (atom >= MIN_STR_ATOM))
158 SetLastError( ERROR_INVALID_PARAMETER );
166 /***********************************************************************
169 * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
171 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
173 return (ATOMENTRY *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
177 /***********************************************************************
178 * InitAtomTable16 (KERNEL.68)
180 WORD WINAPI InitAtomTable16( WORD entries )
186 /* We consider the first table to be initialized as the global table.
187 * This works, as USER (both built-in and native) is the first one to
193 ATOM_UserDS = CURRENT_DS;
194 /* return dummy local handle */
195 return LocalAlloc16( LMEM_FIXED, 1 );
198 /* Allocate the table */
200 if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */
201 handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
202 if (!handle) return 0;
203 table = (ATOMTABLE *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
204 table->size = entries;
205 for (i = 0; i < entries; i++) table->entries[i] = 0;
207 /* Store a pointer to the table in the instance data */
209 ((INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 ))->atomtable = handle;
213 /***********************************************************************
214 * GetAtomHandle (KERNEL.73)
216 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
218 if (atom < MIN_STR_ATOM) return 0;
219 return ATOMTOHANDLE( atom );
223 /***********************************************************************
224 * AddAtom16 (KERNEL.70)
226 * Windows DWORD aligns the atom entry size.
227 * The remaining unused string space created by the alignment
228 * gets padded with '\0's in a certain way to ensure
229 * that at least one trailing '\0' remains.
235 ATOM WINAPI AddAtom16( LPCSTR str )
237 char buffer[MAX_ATOM_LEN+1];
240 ATOMENTRY * entryPtr;
245 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
247 TRACE("%s\n",debugstr_a(buffer));
249 /* Make a copy of the string to be sure it doesn't move in linear memory. */
250 lstrcpynA( buffer, str, sizeof(buffer) );
252 len = strlen( buffer );
253 if (!(table = ATOM_GetTable( TRUE ))) return 0;
254 if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
256 hash = ATOM_Hash( table->size, buffer, len );
257 entry = table->entries[hash];
260 entryPtr = ATOM_MakePtr( entry );
261 if ((entryPtr->length == len) &&
262 (!strncasecmp( entryPtr->str, buffer, len )))
264 entryPtr->refCount++;
265 TRACE("-- existing 0x%x\n", entry);
266 return HANDLETOATOM( entry );
268 entry = entryPtr->next;
271 ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
272 entry = LocalAlloc16( LMEM_FIXED, ae_len );
273 if (!entry) return 0;
274 /* Reload the table ptr in case it moved in linear memory */
275 table = ATOM_GetTable( FALSE );
276 entryPtr = ATOM_MakePtr( entry );
277 entryPtr->next = table->entries[hash];
278 entryPtr->refCount = 1;
279 entryPtr->length = len;
280 /* Some applications _need_ the '\0' padding provided by this strncpy */
281 strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
282 entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
283 table->entries[hash] = entry;
284 TRACE("-- new 0x%x\n", entry);
285 return HANDLETOATOM( entry );
289 /***********************************************************************
290 * DeleteAtom16 (KERNEL.71)
292 ATOM WINAPI DeleteAtom16( ATOM atom )
294 ATOMENTRY * entryPtr;
296 HANDLE16 entry, *prevEntry;
299 if (atom < MIN_STR_ATOM) return 0; /* Integer atom */
300 if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
302 TRACE("0x%x\n",atom);
304 if (!(table = ATOM_GetTable( FALSE ))) return 0;
305 entry = ATOMTOHANDLE( atom );
306 entryPtr = ATOM_MakePtr( entry );
308 /* Find previous atom */
309 hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
310 prevEntry = &table->entries[hash];
311 while (*prevEntry && *prevEntry != entry)
313 ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
314 prevEntry = &prevEntryPtr->next;
316 if (!*prevEntry) return atom;
319 if (--entryPtr->refCount == 0)
321 *prevEntry = entryPtr->next;
322 LocalFree16( entry );
328 /***********************************************************************
329 * FindAtom16 (KERNEL.69)
331 ATOM WINAPI FindAtom16( LPCSTR str )
338 if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
340 TRACE("%s\n",debugres_a(str));
342 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
343 if ((len = strlen( str )) > 255) len = 255;
344 if (!(table = ATOM_GetTable( FALSE ))) return 0;
345 hash = ATOM_Hash( table->size, str, len );
346 entry = table->entries[hash];
349 ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
350 if ((entryPtr->length == len) &&
351 (!strncasecmp( entryPtr->str, str, len )))
353 TRACE("-- found %x\n", entry);
354 return HANDLETOATOM( entry );
356 entry = entryPtr->next;
358 TRACE("-- not found\n");
363 /***********************************************************************
364 * GetAtomName16 (KERNEL.72)
366 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
369 ATOMENTRY * entryPtr;
375 if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
379 if (!count) return 0;
380 if (atom < MIN_STR_ATOM)
382 sprintf( text, "#%d", atom );
388 if (!(table = ATOM_GetTable( FALSE ))) return 0;
389 entry = ATOMTOHANDLE( atom );
390 entryPtr = ATOM_MakePtr( entry );
391 len = entryPtr->length;
392 strPtr = entryPtr->str;
394 if (len >= count) len = count-1;
395 memcpy( buffer, strPtr, len );
400 /***********************************************************************
401 * InitAtomTable (KERNEL32.471)
403 BOOL WINAPI InitAtomTable( DWORD entries )
408 struct init_atom_table_request *req = server_alloc_req( sizeof(*req), 0 );
409 req->entries = entries;
410 ret = !server_call( REQ_INIT_ATOM_TABLE );
417 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
420 if (!ATOM_IsIntAtomA( str, &atom ))
422 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
423 if (len > MAX_ATOM_LEN)
425 SetLastError( ERROR_INVALID_PARAMETER );
430 struct add_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
431 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
433 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
437 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
442 /***********************************************************************
443 * GlobalAddAtomA (USER.268) (KERNEL32.313)
445 * Adds a character string to the global atom table and returns a unique
446 * value identifying the string.
452 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
454 return ATOM_AddAtomA( str, FALSE );
458 /***********************************************************************
459 * AddAtomA (KERNEL32.0)
460 * Adds a string to the atom table and returns the atom identifying the
467 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
469 return ATOM_AddAtomA( str, TRUE );
473 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
476 if (!ATOM_IsIntAtomW( str, &atom ))
478 DWORD len = strlenW(str);
479 if (len > MAX_ATOM_LEN)
481 SetLastError( ERROR_INVALID_PARAMETER );
486 struct add_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
487 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
489 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
493 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
498 /***********************************************************************
499 * GlobalAddAtomW (KERNEL32.314)
501 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
503 return ATOM_AddAtomW( str, FALSE );
507 /***********************************************************************
508 * AddAtomW (KERNEL32.1)
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" : "glbal", atom );
519 if (atom < MIN_STR_ATOM) atom = 0;
524 struct delete_atom_request *req = server_alloc_req( sizeof(*req), 0 );
525 req->atom = atom - MIN_STR_ATOM;
527 if (!server_call( REQ_DELETE_ATOM )) atom = 0;
535 /***********************************************************************
536 * GlobalDeleteAtom (USER.269) (KERNEL32.317)
537 * Decrements the reference count of a string atom. If the count is
538 * zero, the string associated with the atom is removed from the table.
544 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
546 return ATOM_DeleteAtom( atom, FALSE);
550 /***********************************************************************
551 * DeleteAtom (KERNEL32.69)
552 * Decrements the reference count of a string atom. If count becomes
553 * zero, the string associated with the atom is removed from the table.
559 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
561 return ATOM_DeleteAtom( atom, TRUE );
565 static ATOM ATOM_FindAtomA( LPCSTR str, BOOL local )
568 if (!ATOM_IsIntAtomA( str, &atom ))
570 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
571 if (len > MAX_ATOM_LEN)
573 SetLastError( ERROR_INVALID_PARAMETER );
578 struct find_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
579 MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
581 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
585 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
590 /***********************************************************************
591 * GlobalFindAtomA (USER.270) (KERNEL32.318)
593 * Searches the atom table for the string and returns the atom
594 * associated with it.
600 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
602 return ATOM_FindAtomA( str, FALSE );
605 /***********************************************************************
606 * FindAtomA (KERNEL32.117)
607 * Searches the local atom table for the string and returns the atom
608 * associated with that string.
614 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
616 return ATOM_FindAtomA( str, TRUE );
620 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
623 if (!ATOM_IsIntAtomW( str, &atom ))
625 DWORD len = strlenW(str);
626 if (len > MAX_ATOM_LEN)
628 SetLastError( ERROR_INVALID_PARAMETER );
633 struct find_atom_request *req = server_alloc_req( sizeof(*req), len * sizeof(WCHAR) );
634 memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
636 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
640 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
645 /***********************************************************************
646 * GlobalFindAtomW (KERNEL32.319)
648 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
650 return ATOM_FindAtomW( str, FALSE );
654 /***********************************************************************
655 * FindAtomW (KERNEL32.118)
657 ATOM WINAPI FindAtomW( LPCWSTR str )
659 return ATOM_FindAtomW( str, TRUE );
663 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
669 SetLastError( ERROR_MORE_DATA );
672 if (atom < MIN_STR_ATOM)
677 SetLastError( ERROR_INVALID_PARAMETER );
680 len = sprintf( name, "#%d", atom );
681 lstrcpynA( buffer, name, count );
688 struct get_atom_name_request *req = server_alloc_req( sizeof(*req),
689 MAX_ATOM_LEN * sizeof(WCHAR) );
690 req->atom = atom - MIN_STR_ATOM;
692 if (!server_call( REQ_GET_ATOM_NAME ))
694 len = WideCharToMultiByte( CP_ACP, 0, server_data_ptr(req),
695 server_data_size(req) / sizeof(WCHAR),
696 buffer, count - 1, NULL, NULL );
697 if (!len) len = count; /* overflow */
698 else buffer[len] = 0;
704 if (len && count <= len)
706 SetLastError( ERROR_MORE_DATA );
710 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
715 /***********************************************************************
716 * GlobalGetAtomNameA (USER.271) (KERNEL32.323)
718 * Retrieves a copy of the string associated with an atom.
721 * Length of string in characters: Success
724 UINT WINAPI GlobalGetAtomNameA(
725 ATOM atom, /* [in] Atom identifier */
726 LPSTR buffer, /* [out] Pointer to buffer for atom string */
727 INT count ) /* [in] Size of buffer */
729 return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
733 /***********************************************************************
734 * GetAtomNameA (KERNEL32.149)
735 * Retrieves a copy of the string associated with the atom.
738 * Length of string: Success
741 UINT WINAPI GetAtomNameA(
742 ATOM atom, /* [in] Atom */
743 LPSTR buffer, /* [out] Pointer to string for atom string */
744 INT count) /* [in] Size of buffer */
746 return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
750 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
756 SetLastError( ERROR_MORE_DATA );
759 if (atom < MIN_STR_ATOM)
764 SetLastError( ERROR_INVALID_PARAMETER );
767 sprintf( name, "#%d", atom );
768 len = MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, count );
769 if (!len) buffer[count-1] = 0; /* overflow */
776 struct get_atom_name_request *req = server_alloc_req( sizeof(*req),
777 MAX_ATOM_LEN * sizeof(WCHAR) );
778 req->atom = atom - MIN_STR_ATOM;
780 if (!server_call( REQ_GET_ATOM_NAME ))
782 len = server_data_size(req) / sizeof(WCHAR);
783 if (count > len) count = len + 1;
784 memcpy( buffer, server_data_ptr(req), (count-1) * sizeof(WCHAR) );
793 SetLastError( ERROR_MORE_DATA );
796 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
801 /***********************************************************************
802 * GlobalGetAtomNameW (KERNEL32.324)
804 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
806 return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
810 /***********************************************************************
811 * GetAtomNameW (KERNEL32.150)
813 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
815 return ATOM_GetAtomNameW( atom, buffer, count, TRUE );