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
22 #include "wine/winbase16.h"
23 #include "wine/winuser16.h"
27 #include "stackframe.h"
28 #include "debugtools.h"
31 DEFAULT_DEBUG_CHANNEL(atom);
33 #define DEFAULT_ATOMTABLE_SIZE 37
34 #define MIN_STR_ATOM 0xc000
35 #define MAX_ATOM_LEN 255
37 #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << 2)
38 #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> 2)))
54 static WORD ATOM_UserDS = 0; /* USER data segment */
56 /***********************************************************************
59 * Global table initialisation.
61 BOOL ATOM_Init( WORD globalTableSel )
63 ATOM_UserDS = globalTableSel;
68 /***********************************************************************
71 * Return a pointer to the atom table of a given segment, creating
75 * Pointer to table: Success
78 static ATOMTABLE *ATOM_GetTable( BOOL create /* [in] Create */ )
80 INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
83 ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
84 if (table->size) return table;
86 if (!create) return NULL;
87 if (!InitAtomTable16( 0 )) return NULL;
88 /* Reload ptr in case it moved in linear memory */
89 ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
90 return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
94 /***********************************************************************
97 * The hash value for the input string
99 static WORD ATOM_Hash(
100 WORD entries, /* [in] Total number of entries */
101 LPCSTR str, /* [in] Pointer to string to hash */
102 WORD len /* [in] Length of string */
106 TRACE("%x, %s, %x\n", entries, str, len);
108 for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
109 return hash % entries;
113 /***********************************************************************
116 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
119 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
122 if (*atomstr++ != '#') return FALSE;
123 while (*atomstr >= '0' && *atomstr <= '9')
125 atom = atom * 10 + *atomstr - '0';
128 if (*atomstr) return FALSE;
130 if (!atom || (atom >= MIN_STR_ATOM))
132 SetLastError( ERROR_INVALID_PARAMETER );
140 /***********************************************************************
143 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
146 if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
149 if (*atomstr++ != '#') return FALSE;
150 while (*atomstr >= '0' && *atomstr <= '9')
152 atom = atom * 10 + *atomstr - '0';
155 if (*atomstr) return FALSE;
157 if (!atom || (atom >= MIN_STR_ATOM))
159 SetLastError( ERROR_INVALID_PARAMETER );
167 /***********************************************************************
170 * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
172 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
174 return (ATOMENTRY *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
178 /***********************************************************************
179 * InitAtomTable16 (KERNEL.68)
181 WORD WINAPI InitAtomTable16( WORD entries )
187 /* We consider the first table to be initialized as the global table.
188 * This works, as USER (both built-in and native) is the first one to
194 ATOM_UserDS = CURRENT_DS;
195 /* return dummy local handle */
196 return LocalAlloc16( LMEM_FIXED, 1 );
199 /* Allocate the table */
201 if (!entries) entries = DEFAULT_ATOMTABLE_SIZE; /* sanity check */
202 handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
203 if (!handle) return 0;
204 table = (ATOMTABLE *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
205 table->size = entries;
206 for (i = 0; i < entries; i++) table->entries[i] = 0;
208 /* Store a pointer to the table in the instance data */
210 ((INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 ))->atomtable = handle;
214 /***********************************************************************
215 * GetAtomHandle (KERNEL.73)
217 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
219 if (atom < MIN_STR_ATOM) return 0;
220 return ATOMTOHANDLE( atom );
224 /***********************************************************************
225 * AddAtom16 (KERNEL.70)
227 * Windows DWORD aligns the atom entry size.
228 * The remaining unused string space created by the alignment
229 * gets padded with '\0's in a certain way to ensure
230 * that at least one trailing '\0' remains.
236 ATOM WINAPI AddAtom16( LPCSTR str )
238 char buffer[MAX_ATOM_LEN+1];
241 ATOMENTRY * entryPtr;
246 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
248 TRACE("%s\n",debugstr_a(buffer));
250 /* Make a copy of the string to be sure it doesn't move in linear memory. */
251 lstrcpynA( buffer, str, sizeof(buffer) );
253 len = strlen( buffer );
254 if (!(table = ATOM_GetTable( TRUE ))) return 0;
255 if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
257 hash = ATOM_Hash( table->size, buffer, len );
258 entry = table->entries[hash];
261 entryPtr = ATOM_MakePtr( entry );
262 if ((entryPtr->length == len) &&
263 (!lstrncmpiA( entryPtr->str, buffer, len )))
265 entryPtr->refCount++;
266 TRACE("-- existing 0x%x\n", entry);
267 return HANDLETOATOM( entry );
269 entry = entryPtr->next;
272 ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
273 entry = LocalAlloc16( LMEM_FIXED, ae_len );
274 if (!entry) return 0;
275 /* Reload the table ptr in case it moved in linear memory */
276 table = ATOM_GetTable( FALSE );
277 entryPtr = ATOM_MakePtr( entry );
278 entryPtr->next = table->entries[hash];
279 entryPtr->refCount = 1;
280 entryPtr->length = len;
281 /* Some applications _need_ the '\0' padding provided by this strncpy */
282 strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
283 entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
284 table->entries[hash] = entry;
285 TRACE("-- new 0x%x\n", entry);
286 return HANDLETOATOM( entry );
290 /***********************************************************************
291 * DeleteAtom16 (KERNEL.71)
293 ATOM WINAPI DeleteAtom16( ATOM atom )
295 ATOMENTRY * entryPtr;
297 HANDLE16 entry, *prevEntry;
300 if (atom < MIN_STR_ATOM) return 0; /* Integer atom */
301 if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
303 TRACE("0x%x\n",atom);
305 if (!(table = ATOM_GetTable( FALSE ))) return 0;
306 entry = ATOMTOHANDLE( atom );
307 entryPtr = ATOM_MakePtr( entry );
309 /* Find previous atom */
310 hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
311 prevEntry = &table->entries[hash];
312 while (*prevEntry && *prevEntry != entry)
314 ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
315 prevEntry = &prevEntryPtr->next;
317 if (!*prevEntry) return atom;
320 if (--entryPtr->refCount == 0)
322 *prevEntry = entryPtr->next;
323 LocalFree16( entry );
329 /***********************************************************************
330 * FindAtom16 (KERNEL.69)
332 ATOM WINAPI FindAtom16( LPCSTR str )
339 if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
341 TRACE("%s\n",debugres_a(str));
343 if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
344 if ((len = strlen( str )) > 255) len = 255;
345 if (!(table = ATOM_GetTable( FALSE ))) return 0;
346 hash = ATOM_Hash( table->size, str, len );
347 entry = table->entries[hash];
350 ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
351 if ((entryPtr->length == len) &&
352 (!lstrncmpiA( entryPtr->str, str, len )))
354 TRACE("-- found %x\n", entry);
355 return HANDLETOATOM( entry );
357 entry = entryPtr->next;
359 TRACE("-- not found\n");
364 /***********************************************************************
365 * GetAtomName16 (KERNEL.72)
367 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
370 ATOMENTRY * entryPtr;
376 if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
380 if (!count) return 0;
381 if (atom < MIN_STR_ATOM)
383 sprintf( text, "#%d", atom );
389 if (!(table = ATOM_GetTable( FALSE ))) return 0;
390 entry = ATOMTOHANDLE( atom );
391 entryPtr = ATOM_MakePtr( entry );
392 len = entryPtr->length;
393 strPtr = entryPtr->str;
395 if (len >= count) len = count-1;
396 memcpy( buffer, strPtr, len );
401 /***********************************************************************
402 * InitAtomTable (KERNEL32.471)
404 BOOL WINAPI InitAtomTable( DWORD entries )
406 struct init_atom_table_request *req = get_req_buffer();
407 req->entries = entries;
408 return !server_call( REQ_INIT_ATOM_TABLE );
412 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
415 if (!ATOM_IsIntAtomA( str, &atom ))
417 struct add_atom_request *req = get_req_buffer();
418 server_strcpyAtoW( req->name, str );
420 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
422 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
427 /***********************************************************************
428 * GlobalAddAtomA (USER.268) (KERNEL32.313)
430 * Adds a character string to the global atom table and returns a unique
431 * value identifying the string.
437 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
439 return ATOM_AddAtomA( str, FALSE );
443 /***********************************************************************
444 * AddAtomA (KERNEL32.0)
445 * Adds a string to the atom table and returns the atom identifying the
452 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
454 return ATOM_AddAtomA( str, TRUE );
458 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
461 if (!ATOM_IsIntAtomW( str, &atom ))
463 struct add_atom_request *req = get_req_buffer();
464 server_strcpyW( req->name, str );
466 if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
468 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
473 /***********************************************************************
474 * GlobalAddAtomW (KERNEL32.314)
476 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
478 return ATOM_AddAtomW( str, FALSE );
482 /***********************************************************************
483 * AddAtomW (KERNEL32.1)
485 ATOM WINAPI AddAtomW( LPCWSTR str )
487 return ATOM_AddAtomW( str, TRUE );
491 static ATOM ATOM_DeleteAtom( ATOM atom, BOOL local)
493 TRACE( "(%s) %x\n", local ? "local" : "glbal", atom );
494 if (atom < MIN_STR_ATOM) atom = 0;
497 struct delete_atom_request *req = get_req_buffer();
498 req->atom = atom - MIN_STR_ATOM;
500 if (!server_call( REQ_DELETE_ATOM )) atom = 0;
506 /***********************************************************************
507 * GlobalDeleteAtom (USER.269) (KERNEL32.317)
508 * Decrements the reference count of a string atom. If the count is
509 * zero, the string associated with the atom is removed from the table.
515 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
517 return ATOM_DeleteAtom( atom, FALSE);
521 /***********************************************************************
522 * DeleteAtom (KERNEL32.69)
523 * Decrements the reference count of a string atom. If count becomes
524 * zero, the string associated with the atom is removed from the table.
530 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
532 return ATOM_DeleteAtom( atom, TRUE );
536 static ATOM ATOM_FindAtomA( LPCSTR str, BOOL local )
539 if (!ATOM_IsIntAtomA( str, &atom ))
541 struct find_atom_request *req = get_req_buffer();
542 server_strcpyAtoW( req->name, str );
544 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
546 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
551 /***********************************************************************
552 * GlobalFindAtomA (USER.270) (KERNEL32.318)
554 * Searches the atom table for the string and returns the atom
555 * associated with it.
561 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
563 return ATOM_FindAtomA( str, FALSE );
566 /***********************************************************************
567 * FindAtomA (KERNEL32.117)
568 * Searches the local atom table for the string and returns the atom
569 * associated with that string.
575 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
577 return ATOM_FindAtomA( str, TRUE );
581 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
584 if (!ATOM_IsIntAtomW( str, &atom ))
586 struct find_atom_request *req = get_req_buffer();
587 server_strcpyW( req->name, str );
589 if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
591 TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
596 /***********************************************************************
597 * GlobalFindAtomW (KERNEL32.319)
599 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
601 return ATOM_FindAtomW( str, FALSE );
605 /***********************************************************************
606 * FindAtomW (KERNEL32.118)
608 ATOM WINAPI FindAtomW( LPCWSTR str )
610 return ATOM_FindAtomW( str, TRUE );
614 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
617 if (atom < MIN_STR_ATOM)
622 SetLastError( ERROR_INVALID_PARAMETER );
625 len = sprintf( name, "#%d", atom );
626 lstrcpynA( buffer, name, count );
630 struct get_atom_name_request *req = get_req_buffer();
631 req->atom = atom - MIN_STR_ATOM;
633 if (server_call( REQ_GET_ATOM_NAME )) return 0;
634 lstrcpynWtoA( buffer, req->name, count );
635 len = lstrlenW( req->name );
639 SetLastError( ERROR_MORE_DATA );
642 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
647 /***********************************************************************
648 * GlobalGetAtomNameA (USER.271) (KERNEL32.323)
650 * Retrieves a copy of the string associated with an atom.
653 * Length of string in characters: Success
656 UINT WINAPI GlobalGetAtomNameA(
657 ATOM atom, /* [in] Atom identifier */
658 LPSTR buffer, /* [out] Pointer to buffer for atom string */
659 INT count ) /* [in] Size of buffer */
661 return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
665 /***********************************************************************
666 * GetAtomNameA (KERNEL32.149)
667 * Retrieves a copy of the string associated with the atom.
670 * Length of string: Success
673 UINT WINAPI GetAtomNameA(
674 ATOM atom, /* [in] Atom */
675 LPSTR buffer, /* [out] Pointer to string for atom string */
676 INT count) /* [in] Size of buffer */
678 return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
682 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
685 if (atom < MIN_STR_ATOM)
690 SetLastError( ERROR_INVALID_PARAMETER );
693 len = sprintf( name, "#%d", atom );
694 lstrcpynAtoW( buffer, name, count );
698 struct get_atom_name_request *req = get_req_buffer();
699 req->atom = atom - MIN_STR_ATOM;
701 if (server_call( REQ_GET_ATOM_NAME )) return 0;
702 lstrcpynW( buffer, req->name, count );
703 len = lstrlenW( req->name );
707 SetLastError( ERROR_MORE_DATA );
710 TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
715 /***********************************************************************
716 * GlobalGetAtomNameW (KERNEL32.324)
718 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
720 return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
724 /***********************************************************************
725 * GetAtomNameW (KERNEL32.150)
727 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
729 return ATOM_GetAtomNameW( atom, buffer, count, TRUE );