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