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