kernel32: Make some functions hookable.
[wine] / dlls / kernel32 / atom.c
1 /*
2  * Atom table functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winerror.h"
33 #include "winternl.h"
34
35 #include "wine/exception.h"
36 #include "wine/unicode.h"
37 #include "kernel_private.h"
38
39 #define MAX_ATOM_LEN 255
40
41 /******************************************************************
42  *              get_local_table
43  *
44  * Returns the local atom table for this process (and create it if doesn't
45  * exist yet)
46  */
47 static RTL_ATOM_TABLE get_local_table(DWORD entries)
48 {
49     static RTL_ATOM_TABLE local_table;
50
51     if (!local_table)
52     {
53         NTSTATUS        status;
54         RTL_ATOM_TABLE  table = NULL;
55
56         if ((status = RtlCreateAtomTable( entries, &table )))
57             SetLastError( RtlNtStatusToDosError( status ) );
58         else if (InterlockedCompareExchangePointer((void*)&local_table, table, NULL) != NULL)
59             RtlDestroyAtomTable( table );
60     }
61
62     return local_table;
63 }
64
65
66 /***********************************************************************
67  *           InitAtomTable   (KERNEL32.@)
68  *
69  * Initialise the global atom table.
70  *
71  * PARAMS
72  *  entries [I] The number of entries to reserve in the table.
73  *
74  * RETURNS
75  *  Success: TRUE.
76  *  Failure: FALSE.
77  */
78 BOOL WINAPI InitAtomTable( DWORD entries )
79 {
80     return get_local_table( entries ) ? TRUE : FALSE;
81 }
82
83 /******************************************************************
84  *              check_integral_atom
85  *
86  * Check if a string (ANSI or UNICODE) is in fact an integral atom
87  * (but doesn't check the "#1234" form)
88  */
89 static inline BOOL check_integral_atom( const void* ptr, ATOM* patom)
90 {
91     if (HIWORD( ptr )) return FALSE;
92     if ((*patom = LOWORD( ptr )) >= MAXINTATOM)
93     {
94         SetLastError( ERROR_INVALID_PARAMETER );
95         *patom = 0;
96     }
97     return TRUE;
98 }
99
100 /***********************************************************************
101  *           GlobalAddAtomA   (KERNEL32.@)
102  *
103  * Add a character string to the global atom table and return a unique
104  * value identifying it.
105  *
106  * RETURNS
107  *      Success: The atom allocated to str.
108  *      Failure: 0.
109  */
110 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] String to add */ )
111 {
112     ATOM atom = 0;
113     __TRY
114     {
115         if (!check_integral_atom( str, &atom ))
116         {
117             WCHAR buffer[MAX_ATOM_LEN];
118             DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), buffer, MAX_ATOM_LEN );
119             if (!len) SetLastError( ERROR_INVALID_PARAMETER );
120             else
121             {
122                 NTSTATUS status = NtAddAtom( buffer, len * sizeof(WCHAR), &atom );
123                 if (status)
124                 {
125                     SetLastError( RtlNtStatusToDosError( status ) );
126                     atom = 0;
127                 }
128             }
129         }
130     }
131     __EXCEPT_PAGE_FAULT
132     {
133         SetLastError( ERROR_INVALID_PARAMETER );
134         atom = 0;
135     }
136     __ENDTRY
137     return atom;
138 }
139
140
141 /***********************************************************************
142  *           AddAtomA   (KERNEL32.@)
143  *
144  * Add a character string to the global atom table and return a unique
145  * value identifying it.
146  *
147  * RETURNS
148  *      Success: The atom allocated to str.
149  *      Failure: 0.
150  */
151 ATOM WINAPI AddAtomA( LPCSTR str /* [in] String to add */ )
152 {
153     ATOM atom = 0;
154
155     if (!check_integral_atom( str, &atom ))
156     {
157         WCHAR           buffer[MAX_ATOM_LEN + 1];
158         DWORD           len;
159         RTL_ATOM_TABLE  table;
160
161         len = MultiByteToWideChar( CP_ACP, 0, str, -1, buffer, MAX_ATOM_LEN + 1 );
162         if (!len) SetLastError( ERROR_INVALID_PARAMETER );
163         else if ((table = get_local_table( 0 )))
164         {
165             NTSTATUS status = RtlAddAtomToAtomTable( table, buffer, &atom );
166             if (status)
167             {
168                 SetLastError( RtlNtStatusToDosError( status ) );
169                 atom = 0;
170             }
171         }
172     }
173     return atom;
174 }
175
176 /***********************************************************************
177  *           GlobalAddAtomW   (KERNEL32.@)
178  *
179  * Unicode version of GlobalAddAtomA.
180  */
181 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
182 {
183     ATOM        atom = 0;
184     NTSTATUS    status;
185
186     if (!check_integral_atom( str, &atom ) && 
187         (status = NtAddAtom( str, strlenW( str ) * sizeof(WCHAR), &atom )))
188     {
189         SetLastError( RtlNtStatusToDosError( status ) );
190         atom = 0;
191     }
192     return atom;
193 }
194
195
196 /***********************************************************************
197  *           AddAtomW   (KERNEL32.@)
198  *
199  * Unicode version of AddAtomA.          
200  */
201 ATOM WINAPI AddAtomW( LPCWSTR str )
202 {
203     ATOM                atom = 0;
204     RTL_ATOM_TABLE      table;
205
206     if (!check_integral_atom( str, &atom ) && (table = get_local_table( 0 )))
207     {
208         NTSTATUS status = RtlAddAtomToAtomTable( table, str, &atom );
209         if (status)
210         {
211             SetLastError( RtlNtStatusToDosError( status ) );
212             atom = 0;
213         }
214     }
215     return atom;
216 }
217
218
219 /***********************************************************************
220  *           GlobalDeleteAtom   (KERNEL32.@)
221  *
222  * Decrement the reference count of a string atom.  If the count is
223  * zero, the string associated with the atom is removed from the table.
224  *
225  * RETURNS
226  *      Success: 0.
227  *      Failure: atom.
228  */
229 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
230 {
231     if (atom >= MAXINTATOM)
232     {
233         NTSTATUS status = NtDeleteAtom( atom );
234         if (status)
235         {
236             SetLastError( RtlNtStatusToDosError( status ) );
237             return atom;
238         }
239     }
240     return 0;
241 }
242
243
244 /***********************************************************************
245  *           DeleteAtom   (KERNEL32.@)
246  *
247  * Decrement the reference count of a string atom.  If the count becomes
248  * zero, the string associated with the atom is removed from the table.
249  *
250  * RETURNS
251  *      Success: 0.
252  *      Failure: atom
253  */
254 ATOM WINAPI DeleteAtom( ATOM atom /* [in] Atom to delete */ )
255 {
256     NTSTATUS            status;
257     RTL_ATOM_TABLE      table;
258
259     if (atom >= MAXINTATOM)
260     {
261         if (!(table = get_local_table( 0 ))) return atom;
262         status = RtlDeleteAtomFromAtomTable( table, atom );
263         if (status)
264         {
265             SetLastError( RtlNtStatusToDosError( status ) );
266             return atom;
267         }
268     }
269     return 0;
270 }
271
272
273 /***********************************************************************
274  *           GlobalFindAtomA   (KERNEL32.@)
275  *
276  * Get the atom associated with a string.
277  *
278  * RETURNS
279  *      Success: The associated atom.
280  *      Failure: 0.
281  */
282 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
283 {
284     ATOM atom = 0;
285
286     if (!check_integral_atom( str, &atom ))
287     {
288         WCHAR buffer[MAX_ATOM_LEN];
289         DWORD len = MultiByteToWideChar( CP_ACP, 0, str, strlen(str), buffer, MAX_ATOM_LEN );
290
291         if (!len) SetLastError( ERROR_INVALID_PARAMETER );
292         else
293         {
294             NTSTATUS status = NtFindAtom( buffer, len * sizeof(WCHAR), &atom );
295             if (status)
296             {
297                 SetLastError( RtlNtStatusToDosError( status ) );
298                 atom = 0;
299             }
300         }
301     }
302     return atom;
303 }
304
305 /***********************************************************************
306  *           FindAtomA   (KERNEL32.@)
307  *
308  * Get the atom associated with a string.
309  *
310  * RETURNS
311  *      Success: The associated atom.
312  *      Failure: 0.
313  */
314 ATOM WINAPI FindAtomA( LPCSTR str /* [in] Pointer to string to find */ )
315 {
316     ATOM atom = 0;
317
318     if (!check_integral_atom( str, &atom ))
319     {
320         WCHAR           buffer[MAX_ATOM_LEN + 1];
321         DWORD           len;
322         RTL_ATOM_TABLE  table;
323
324         len = MultiByteToWideChar( CP_ACP, 0, str, -1, buffer, MAX_ATOM_LEN + 1 );
325         if (!len) SetLastError( ERROR_INVALID_PARAMETER );
326         else if ((table = get_local_table( 0 )))
327         {
328             NTSTATUS status = RtlLookupAtomInAtomTable( table, buffer, &atom );
329             if (status)
330             {
331                 SetLastError( RtlNtStatusToDosError( status ) );
332                 atom = 0;
333             }
334         }
335     }
336     return atom;
337 }
338
339
340 /***********************************************************************
341  *           GlobalFindAtomW   (KERNEL32.@)
342  *
343  * Unicode version of GlobalFindAtomA.
344  */
345 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
346 {
347     ATOM atom = 0;
348
349     if (!check_integral_atom( str, &atom ))
350     {
351         NTSTATUS status = NtFindAtom( str, strlenW( str ) * sizeof(WCHAR), &atom );
352         if (status)
353         {
354             SetLastError( RtlNtStatusToDosError( status ) );
355             atom = 0;
356         }
357     }
358     return atom;
359 }
360
361
362 /***********************************************************************
363  *           FindAtomW   (KERNEL32.@)
364  *
365  * Unicode version of FindAtomA.
366  */
367 ATOM WINAPI FindAtomW( LPCWSTR str )
368 {
369     ATOM                atom = 0;
370     NTSTATUS            status;
371     RTL_ATOM_TABLE      table;
372
373     if ((table = get_local_table( 0 )))
374     {
375         status = RtlLookupAtomInAtomTable( table, str, &atom );
376         if (status)
377         {
378             SetLastError( RtlNtStatusToDosError( status ) );
379             atom = 0;
380         }
381     }
382     return atom;
383 }
384
385
386 /***********************************************************************
387  *           GlobalGetAtomNameA   (KERNEL32.@)
388  *
389  * Get a copy of the string associated with an atom.
390  *
391  * RETURNS
392  *      Success: The length of the returned string in characters.
393  *      Failure: 0.
394  */
395 UINT WINAPI GlobalGetAtomNameA(
396               ATOM atom,    /* [in]  Atom identifier */
397               LPSTR buffer, /* [out] Pointer to buffer for atom string */
398               INT count )   /* [in]  Size of buffer */
399 {
400     WCHAR       tmpW[MAX_ATOM_LEN + 1];
401     UINT        wlen, len = 0, c;
402
403     if (count <= 0) SetLastError( ERROR_MORE_DATA );
404     else if ((wlen = GlobalGetAtomNameW( atom, tmpW, MAX_ATOM_LEN + 1 )))
405     {
406         char    tmp[MAX_ATOM_LEN + 1];
407
408         len = WideCharToMultiByte( CP_ACP, 0, tmpW, wlen, tmp, MAX_ATOM_LEN + 1, NULL, NULL );
409         c = min(len, count - 1);
410         memcpy(buffer, tmp, c);
411         buffer[c] = '\0';
412         if (len >= count)
413         {
414             len = 0;
415             SetLastError( ERROR_MORE_DATA );
416         }
417     }
418     return len;
419 }
420
421
422 /***********************************************************************
423  *           GetAtomNameA   (KERNEL32.@)
424  *
425  * Get a copy of the string associated with an atom.
426  *
427  * RETURNS
428  *      Success: The length of the returned string in characters.
429  *      Failure: 0.
430  */
431 UINT WINAPI GetAtomNameA(
432               ATOM atom,    /* [in]  Atom */
433               LPSTR buffer, /* [out] Pointer to string for atom string */
434               INT count)    /* [in]  Size of buffer */
435 {
436     WCHAR       tmpW[MAX_ATOM_LEN + 1];
437     UINT        wlen, len = 0, c;
438
439     if (count <= 0) SetLastError( ERROR_MORE_DATA );
440     else if ((wlen = GetAtomNameW( atom, tmpW, MAX_ATOM_LEN + 1 )))
441     {
442         char    tmp[MAX_ATOM_LEN + 1];
443
444         len = WideCharToMultiByte( CP_ACP, 0, tmpW, wlen, tmp, MAX_ATOM_LEN + 1, NULL, NULL );
445         c = min(len, count - 1);
446         memcpy(buffer, tmp, c);
447         buffer[c] = '\0';
448         if (len >= count)
449         {
450             len = c;
451             SetLastError( ERROR_MORE_DATA );
452         }
453     }
454     return len;
455 }
456
457
458 /***********************************************************************
459  *           GlobalGetAtomNameW   (KERNEL32.@)
460  *
461  * Unicode version of GlobalGetAtomNameA.
462  */
463 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
464 {
465     char        ptr[sizeof(ATOM_BASIC_INFORMATION) + MAX_ATOM_LEN * sizeof(WCHAR)];
466     ATOM_BASIC_INFORMATION*     abi = (ATOM_BASIC_INFORMATION*)ptr;
467     ULONG       ptr_size = sizeof(ATOM_BASIC_INFORMATION) + MAX_ATOM_LEN * sizeof(WCHAR);
468     NTSTATUS    status;
469     UINT        length = 0;
470
471     if (count <= 0)
472     {
473         SetLastError( ERROR_MORE_DATA );
474         return 0;
475     }
476     status = NtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL );
477     if (status) SetLastError( RtlNtStatusToDosError( status ) );
478     else
479     {
480         length = min( abi->NameLength / sizeof(WCHAR), count);
481         memcpy( buffer, abi->Name, length * sizeof(WCHAR) );
482         /* yes, the string will not be null terminated if the passed buffer
483          * is one WCHAR too small (and it's not an error)
484          */
485         if (length < abi->NameLength / sizeof(WCHAR))
486         {
487             SetLastError( ERROR_MORE_DATA );
488             length = count;
489         }
490         else if (length < count) buffer[length] = '\0';
491     }
492     return length;
493 }
494
495
496 /***********************************************************************
497  *           GetAtomNameW   (KERNEL32.@)
498  *
499  * Unicode version of GetAtomNameA.
500  */
501 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
502 {
503     NTSTATUS            status;
504     RTL_ATOM_TABLE      table;
505     DWORD               length;
506     WCHAR               tmp[MAX_ATOM_LEN + 1];
507
508     if (count <= 0)
509     {
510         SetLastError( ERROR_MORE_DATA );
511         return 0;
512     }
513     if (!(table = get_local_table( 0 ))) return 0;
514     length = sizeof(tmp);
515     status = RtlQueryAtomInAtomTable( table, atom, NULL, NULL, tmp, &length );
516     if (status)
517     {
518         SetLastError( RtlNtStatusToDosError( status ) );
519         return 0;
520     }
521     length = min(length, (count - 1) * sizeof(WCHAR));
522     if (length) memcpy(buffer, tmp, length);
523     else SetLastError( ERROR_INSUFFICIENT_BUFFER );
524     length /= sizeof(WCHAR);
525     buffer[length] = '\0';
526     return length;
527 }