Fixed some issues found by winapi_check.
[wine] / memory / atom.c
1 /*
2  * Atom table functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  */
6
7 /*
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
11  * have to be changed.
12  */
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 #include "windef.h"
20 #include "winnls.h"
21 #include "wine/winbase16.h"
22 #include "wine/unicode.h"
23 #include "winerror.h"
24 #include "instance.h"
25 #include "stackframe.h"
26 #include "debugtools.h"
27 #include "server.h"
28
29 DEFAULT_DEBUG_CHANNEL(atom);
30
31 #define DEFAULT_ATOMTABLE_SIZE    37
32 #define MIN_STR_ATOM              0xc000
33 #define MAX_ATOM_LEN              255
34
35 #define ATOMTOHANDLE(atom)        ((HANDLE16)(atom) << 2)
36 #define HANDLETOATOM(handle)      ((ATOM)(0xc000 | ((handle) >> 2)))
37
38 typedef struct
39 {
40     HANDLE16    next;
41     WORD        refCount;
42     BYTE        length;
43     BYTE        str[1];
44 } ATOMENTRY;
45
46 typedef struct
47 {
48     WORD        size;
49     HANDLE16    entries[1];
50 } ATOMTABLE;
51                 
52 static WORD ATOM_UserDS = 0;  /* USER data segment */
53
54 /***********************************************************************
55  *           ATOM_Init
56  *
57  * Global table initialisation.
58  */
59 BOOL ATOM_Init( WORD globalTableSel )
60 {
61     ATOM_UserDS = globalTableSel;
62     return TRUE;
63 }
64
65
66 /***********************************************************************
67  *           ATOM_GetTable
68  *
69  * Return a pointer to the atom table of a given segment, creating
70  * it if necessary.
71  *
72  * RETURNS
73  *      Pointer to table: Success
74  *      NULL: Failure
75  */
76 static ATOMTABLE *ATOM_GetTable( BOOL create  /* [in] Create */ )
77 {
78     INSTANCEDATA *ptr = MapSL( MAKESEGPTR( CURRENT_DS, 0 ) );
79     if (ptr->atomtable)
80     {
81         ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
82         if (table->size) return table;
83     }
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);
89 }
90
91
92 /***********************************************************************
93  *           ATOM_Hash
94  * RETURNS
95  *      The hash value for the input string
96  */
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 */
101 ) {
102     WORD i, hash = 0;
103
104     TRACE("%x, %s, %x\n", entries, str, len);
105     
106     for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
107     return hash % entries;
108 }
109
110
111 /***********************************************************************
112  *           ATOM_IsIntAtomA
113  */
114 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
115 {
116     UINT atom = 0;
117     if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
118     else
119     {
120         if (*atomstr++ != '#') return FALSE;
121         while (*atomstr >= '0' && *atomstr <= '9')
122         {
123             atom = atom * 10 + *atomstr - '0';
124             atomstr++;
125         }
126         if (*atomstr) return FALSE;
127     }
128     if (!atom || (atom >= MIN_STR_ATOM))
129     {
130         SetLastError( ERROR_INVALID_PARAMETER );
131         atom = 0;
132     }
133     *atomid = atom;
134     return TRUE;
135 }
136
137
138 /***********************************************************************
139  *           ATOM_IsIntAtomW
140  */
141 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
142 {
143     UINT atom = 0;
144     if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
145     else
146     {
147         if (*atomstr++ != '#') return FALSE;
148         while (*atomstr >= '0' && *atomstr <= '9')
149         {
150             atom = atom * 10 + *atomstr - '0';
151             atomstr++;
152         }
153         if (*atomstr) return FALSE;
154     }
155     if (!atom || (atom >= MIN_STR_ATOM))
156     {
157         SetLastError( ERROR_INVALID_PARAMETER );
158         atom = 0;
159     }
160     *atomid = atom;
161     return TRUE;
162 }
163
164
165 /***********************************************************************
166  *           ATOM_MakePtr
167  *
168  * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
169  */
170 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
171 {
172     return MapSL( MAKESEGPTR( CURRENT_DS, handle ) );
173 }
174
175
176 /***********************************************************************
177  *           InitAtomTable16   (KERNEL.68)
178  */
179 WORD WINAPI InitAtomTable16( WORD entries )
180 {
181     int i;
182     HANDLE16 handle;
183     ATOMTABLE *table;
184
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 
187        * register ... 
188        */
189
190     if (!ATOM_UserDS)
191     {
192         ATOM_UserDS = CURRENT_DS; 
193         /* return dummy local handle */
194         return LocalAlloc16( LMEM_FIXED, 1 );
195     }
196
197       /* Allocate the table */
198
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;
205
206       /* Store a pointer to the table in the instance data */
207
208     ((INSTANCEDATA *)MapSL( MAKESEGPTR( CURRENT_DS, 0 )))->atomtable = handle;
209     return handle;
210 }
211
212 /***********************************************************************
213  *           GetAtomHandle   (KERNEL.73)
214  */
215 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
216 {
217     if (atom < MIN_STR_ATOM) return 0;
218     return ATOMTOHANDLE( atom );
219 }
220
221
222 /***********************************************************************
223  *           AddAtom16   (KERNEL.70)
224  *
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.
229  *
230  * RETURNS
231  *      Atom: Success
232  *      0: Failure
233  */
234 ATOM WINAPI AddAtom16( LPCSTR str )
235 {
236     char buffer[MAX_ATOM_LEN+1];
237     WORD hash;
238     HANDLE16 entry;
239     ATOMENTRY * entryPtr;
240     ATOMTABLE * table;
241     int len, ae_len;
242     WORD        iatom;
243
244     if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
245
246     TRACE("%s\n",debugstr_a(buffer));
247     
248     /* Make a copy of the string to be sure it doesn't move in linear memory. */
249     lstrcpynA( buffer, str, sizeof(buffer) );
250
251     len = strlen( buffer );
252     if (!(table = ATOM_GetTable( TRUE ))) return 0;
253     if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
254
255     hash = ATOM_Hash( table->size, buffer, len );
256     entry = table->entries[hash];
257     while (entry)
258     {
259         entryPtr = ATOM_MakePtr( entry );
260         if ((entryPtr->length == len) && 
261             (!strncasecmp( entryPtr->str, buffer, len )))
262         {
263             entryPtr->refCount++;
264             TRACE("-- existing 0x%x\n", entry);
265             return HANDLETOATOM( entry );
266         }
267         entry = entryPtr->next;
268     }
269
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 );
285 }
286
287
288 /***********************************************************************
289  *           DeleteAtom16   (KERNEL.71)
290  */
291 ATOM WINAPI DeleteAtom16( ATOM atom )
292 {
293     ATOMENTRY * entryPtr;
294     ATOMTABLE * table;
295     HANDLE16 entry, *prevEntry;
296     WORD hash;
297
298     if (atom < MIN_STR_ATOM) return 0;  /* Integer atom */
299     if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
300
301     TRACE("0x%x\n",atom);
302
303     if (!(table = ATOM_GetTable( FALSE ))) return 0;
304     entry = ATOMTOHANDLE( atom );
305     entryPtr = ATOM_MakePtr( entry );
306
307     /* Find previous atom */
308     hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
309     prevEntry = &table->entries[hash];
310     while (*prevEntry && *prevEntry != entry)
311     {
312         ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
313         prevEntry = &prevEntryPtr->next;
314     }    
315     if (!*prevEntry) return atom;
316
317     /* Delete atom */
318     if (--entryPtr->refCount == 0)
319     {
320         *prevEntry = entryPtr->next;
321         LocalFree16( entry );
322     }    
323     return 0;
324 }
325
326
327 /***********************************************************************
328  *           FindAtom16   (KERNEL.69)
329  */
330 ATOM WINAPI FindAtom16( LPCSTR str )
331 {
332     ATOMTABLE * table;
333     WORD hash,iatom;
334     HANDLE16 entry;
335     int len;
336
337     if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
338
339     TRACE("%s\n",debugres_a(str));
340
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];
346     while (entry)
347     {
348         ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
349         if ((entryPtr->length == len) && 
350             (!strncasecmp( entryPtr->str, str, len )))
351         {
352             TRACE("-- found %x\n", entry);
353             return HANDLETOATOM( entry );
354         }
355         entry = entryPtr->next;
356     }
357     TRACE("-- not found\n");
358     return 0;
359 }
360
361
362 /***********************************************************************
363  *           GetAtomName16   (KERNEL.72)
364  */
365 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
366 {
367     ATOMTABLE * table;
368     ATOMENTRY * entryPtr;
369     HANDLE16 entry;
370     char * strPtr;
371     UINT len;
372     char text[8];
373
374     if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
375     
376     TRACE("%x\n",atom);
377     
378     if (!count) return 0;
379     if (atom < MIN_STR_ATOM)
380     {
381         sprintf( text, "#%d", atom );
382         len = strlen(text);
383         strPtr = text;
384     }
385     else
386     {
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;
392     }
393     if (len >= count) len = count-1;
394     memcpy( buffer, strPtr, len );
395     buffer[len] = '\0';
396     return len;
397 }
398
399 /***********************************************************************
400  *           InitAtomTable   (KERNEL32.@)
401  */
402 BOOL WINAPI InitAtomTable( DWORD entries )
403 {
404     BOOL ret;
405     SERVER_START_REQ( init_atom_table )
406     {
407         req->entries = entries;
408         ret = !SERVER_CALL_ERR();
409     }
410     SERVER_END_REQ;
411     return ret;
412 }
413
414
415 static ATOM ATOM_AddAtomA( LPCSTR str, BOOL local )
416 {
417     ATOM atom = 0;
418     if (!ATOM_IsIntAtomA( str, &atom ))
419     {
420         DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
421         if (len > MAX_ATOM_LEN)
422         {
423             SetLastError( ERROR_INVALID_PARAMETER );
424             return 0;
425         }
426         SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
427         {
428             MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
429             req->local = local;
430             if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
431         }
432         SERVER_END_VAR_REQ;
433     }
434     TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
435     return atom;
436 }
437
438
439 /***********************************************************************
440  *           GlobalAddAtomA   (USER.268)
441  *           GlobalAddAtomA   (KERNEL32.@)
442  *
443  * Adds a character string to the global atom table and returns a unique
444  * value identifying the string.
445  *
446  * RETURNS
447  *      Atom: Success
448  *      0: Failure
449  */
450 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
451 {
452     return ATOM_AddAtomA( str, FALSE );
453 }
454
455
456 /***********************************************************************
457  *           AddAtomA   (KERNEL32.@)
458  * Adds a string to the atom table and returns the atom identifying the
459  * string.
460  *
461  * RETURNS
462  *      Atom: Success
463  *      0: Failure
464  */
465 ATOM WINAPI AddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
466 {
467     return ATOM_AddAtomA( str, TRUE );
468 }
469
470
471 static ATOM ATOM_AddAtomW( LPCWSTR str, BOOL local )
472 {
473     ATOM atom = 0;
474     if (!ATOM_IsIntAtomW( str, &atom ))
475     {
476         DWORD len = strlenW(str);
477         if (len > MAX_ATOM_LEN)
478         {
479             SetLastError( ERROR_INVALID_PARAMETER );
480             return 0;
481         }
482         SERVER_START_VAR_REQ( add_atom, len * sizeof(WCHAR) )
483         {
484             memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
485             req->local = local;
486             if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
487         }
488         SERVER_END_VAR_REQ;
489     }
490     TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
491     return atom;
492 }
493
494
495 /***********************************************************************
496  *           GlobalAddAtomW   (KERNEL32.@)
497  */
498 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
499 {
500     return ATOM_AddAtomW( str, FALSE );
501 }
502
503
504 /***********************************************************************
505  *           AddAtomW   (KERNEL32.@)
506  */
507 ATOM WINAPI AddAtomW( LPCWSTR str )
508 {
509     return ATOM_AddAtomW( str, TRUE );
510 }
511
512
513 static ATOM ATOM_DeleteAtom( ATOM atom,  BOOL local)
514 {
515     TRACE( "(%s) %x\n", local ? "local" : "global", atom );
516     if (atom < MIN_STR_ATOM) atom = 0;
517     else
518     {
519         SERVER_START_REQ( delete_atom )
520         {
521             req->atom = atom - MIN_STR_ATOM;
522             req->local = local;
523             if (!SERVER_CALL_ERR()) atom = 0;
524         }
525         SERVER_END_REQ;
526     }
527     return atom;
528 }
529
530
531 /***********************************************************************
532  *           GlobalDeleteAtom   (USER.269)
533  *           GlobalDeleteAtom   (KERNEL32.@)
534  * Decrements the reference count of a string atom.  If the count is
535  * zero, the string associated with the atom is removed from the table.
536  *
537  * RETURNS
538  *      0: Success
539  *      Atom: Failure
540  */
541 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
542 {
543     return ATOM_DeleteAtom( atom, FALSE);
544 }
545
546
547 /***********************************************************************
548  *           DeleteAtom   (KERNEL32.@)
549  * Decrements the reference count of a string atom.  If count becomes
550  * zero, the string associated with the atom is removed from the table.
551  *
552  * RETURNS
553  *      0: Success
554  *      Atom: Failure
555  */
556 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
557 {
558     return ATOM_DeleteAtom( atom, TRUE ); 
559 }
560
561
562 static ATOM ATOM_FindAtomA( LPCSTR str, BOOL local )
563 {
564     ATOM atom = 0;
565     if (!ATOM_IsIntAtomA( str, &atom ))
566     {
567         DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), NULL, 0 );
568         if (len > MAX_ATOM_LEN)
569         {
570             SetLastError( ERROR_INVALID_PARAMETER );
571             return 0;
572         }
573         SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
574         {
575             MultiByteToWideChar( CP_ACP, 0, str, strlen(str), server_data_ptr(req), len );
576             req->local = local;
577             if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
578         }
579         SERVER_END_VAR_REQ;
580     }
581     TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_a(str), atom );
582     return atom;
583 }
584
585
586 /***********************************************************************
587  *           GlobalFindAtomA   (USER.270)
588  *           GlobalFindAtomA   (KERNEL32.@)
589  *
590  * Searches the atom table for the string and returns the atom
591  * associated with it.
592  *
593  * RETURNS
594  *      Atom: Success
595  *      0: Failure
596  */
597 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
598 {
599     return ATOM_FindAtomA( str, FALSE );
600 }
601
602 /***********************************************************************
603  *           FindAtomA   (KERNEL32.@)
604  * Searches the local atom table for the string and returns the atom
605  * associated with that string.
606  *
607  * RETURNS
608  *      Atom: Success
609  *      0: Failure
610  */
611 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
612 {
613     return ATOM_FindAtomA( str, TRUE );
614 }
615
616
617 static ATOM ATOM_FindAtomW( LPCWSTR str, BOOL local )
618 {
619     ATOM atom = 0;
620     if (!ATOM_IsIntAtomW( str, &atom ))
621     {
622         DWORD len = strlenW(str);
623         if (len > MAX_ATOM_LEN)
624         {
625             SetLastError( ERROR_INVALID_PARAMETER );
626             return 0;
627         }
628         SERVER_START_VAR_REQ( find_atom, len * sizeof(WCHAR) )
629         {
630             memcpy( server_data_ptr(req), str, len * sizeof(WCHAR) );
631             req->local = local;
632             if (!SERVER_CALL_ERR()) atom = req->atom + MIN_STR_ATOM;
633         }
634         SERVER_END_VAR_REQ;
635     }
636     TRACE( "(%s) %s -> %x\n", local ? "local" : "global", debugres_w(str), atom );
637     return atom;
638 }
639
640
641 /***********************************************************************
642  *           GlobalFindAtomW   (KERNEL32.@)
643  */
644 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
645 {
646     return ATOM_FindAtomW( str, FALSE );
647 }
648
649
650 /***********************************************************************
651  *           FindAtomW   (KERNEL32.@)
652  */
653 ATOM WINAPI FindAtomW( LPCWSTR str )
654 {
655     return ATOM_FindAtomW( str, TRUE );
656 }
657
658
659 static UINT ATOM_GetAtomNameA( ATOM atom, LPSTR buffer, INT count, BOOL local )
660 {
661     INT len;
662
663     if (count <= 0)
664     {
665         SetLastError( ERROR_MORE_DATA );
666         return 0;
667     }
668     if (atom < MIN_STR_ATOM)
669     {
670         char name[8];
671         if (!atom)
672         {
673             SetLastError( ERROR_INVALID_PARAMETER );
674             return 0;
675         }
676         len = sprintf( name, "#%d", atom );
677         lstrcpynA( buffer, name, count );
678     }
679     else
680     {
681         len = 0;
682         SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
683         {
684             req->atom = atom - MIN_STR_ATOM;
685             req->local = local;
686             if (!SERVER_CALL_ERR())
687             {
688                 len = WideCharToMultiByte( CP_ACP, 0, server_data_ptr(req),
689                                            server_data_size(req) / sizeof(WCHAR),
690                                            buffer, count - 1, NULL, NULL );
691                 if (!len) len = count; /* overflow */
692                 else buffer[len] = 0;
693             }
694         }
695         SERVER_END_VAR_REQ;
696     }
697
698     if (len && count <= len)
699     {
700         SetLastError( ERROR_MORE_DATA );
701         buffer[count-1] = 0;
702         return 0;
703     }
704     TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_a(buffer) );
705     return len;
706 }
707
708
709 /***********************************************************************
710  *           GlobalGetAtomNameA   (USER.271)
711  *           GlobalGetAtomNameA   (KERNEL32.@)
712  *
713  * Retrieves a copy of the string associated with an atom.
714  *
715  * RETURNS
716  *      Length of string in characters: Success
717  *      0: Failure
718  */
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 */
723 {
724     return ATOM_GetAtomNameA( atom, buffer, count, FALSE );
725 }
726
727
728 /***********************************************************************
729  *           GetAtomNameA   (KERNEL32.@)
730  * Retrieves a copy of the string associated with the atom.
731  *
732  * RETURNS
733  *      Length of string: Success
734  *      0: Failure
735  */
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 */
740 {
741     return ATOM_GetAtomNameA( atom, buffer, count, TRUE );
742 }
743
744
745 static UINT ATOM_GetAtomNameW( ATOM atom, LPWSTR buffer, INT count, BOOL local )
746 {
747     INT len;
748
749     if (count <= 0)
750     {
751         SetLastError( ERROR_MORE_DATA );
752         return 0;
753     }
754     if (atom < MIN_STR_ATOM)
755     {
756         char name[8];
757         if (!atom)
758         {
759             SetLastError( ERROR_INVALID_PARAMETER );
760             return 0;
761         }
762         sprintf( name, "#%d", atom );
763         len = MultiByteToWideChar( CP_ACP, 0, name, -1, buffer, count );
764         if (!len) buffer[count-1] = 0;  /* overflow */
765     }
766     else
767     {
768         len = 0;
769         SERVER_START_VAR_REQ( get_atom_name, MAX_ATOM_LEN * sizeof(WCHAR) )
770         {
771             req->atom = atom - MIN_STR_ATOM;
772             req->local = local;
773             if (!SERVER_CALL_ERR())
774             {
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) );
778                 buffer[count-1] = 0;
779             }
780         }
781         SERVER_END_VAR_REQ;
782         if (!len) return 0;
783     }
784     if (count <= len)
785     {
786         SetLastError( ERROR_MORE_DATA );
787         return 0;
788     }
789     TRACE( "(%s) %x -> %s\n", local ? "local" : "global", atom, debugstr_w(buffer) );
790     return len;
791 }
792
793
794 /***********************************************************************
795  *           GlobalGetAtomNameW   (KERNEL32.@)
796  */
797 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
798 {
799     return ATOM_GetAtomNameW( atom, buffer, count, FALSE);
800 }
801
802
803 /***********************************************************************
804  *           GetAtomNameW   (KERNEL32.@)
805  */
806 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
807 {
808     return ATOM_GetAtomNameW( atom, buffer, count, TRUE );
809 }