ntdll: Always enable tail checking when running under Valgrind.
[wine] / dlls / ntdll / atom.c
1 /*
2  * Atom table functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  * Copyright 2004,2005 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #include "ntstatus.h"
32 #define WIN32_NO_STATUS
33 #include "windef.h"
34
35 #include "wine/server.h"
36 #include "wine/unicode.h"
37
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(atom);
41
42 #define MAX_ATOM_LEN              255
43
44 /******************************************************************
45  *              is_integral_atom
46  * Returns STATUS_SUCCESS if integral atom and 'pAtom' is filled
47  *         STATUS_INVALID_PARAMETER if 'atomstr' is too long
48  *         STATUS_MORE_ENTRIES otherwise
49  */
50 static NTSTATUS is_integral_atom( LPCWSTR atomstr, size_t len, RTL_ATOM* pAtom )
51 {
52     RTL_ATOM atom;
53
54     if (HIWORD( atomstr ))
55     {
56         const WCHAR* ptr = atomstr;
57         if (!len) return STATUS_OBJECT_NAME_INVALID;
58
59         if (*ptr++ == '#')
60         {
61             atom = 0;
62             while (ptr < atomstr + len && *ptr >= '0' && *ptr <= '9')
63             {
64                 atom = atom * 10 + *ptr++ - '0';
65             }
66             if (ptr > atomstr + 1 && ptr == atomstr + len) goto done;
67         }
68         if (len > MAX_ATOM_LEN) return STATUS_INVALID_PARAMETER;
69         return STATUS_MORE_ENTRIES;
70     }
71     else atom = LOWORD( atomstr );
72 done:
73     if (!atom || atom >= MAXINTATOM) return STATUS_INVALID_PARAMETER;
74     *pAtom = atom;
75     return STATUS_SUCCESS;
76 }
77
78 /******************************************************************
79  *              RtlDeleteAtomFromAtomTable (NTDLL.@)
80  */
81 NTSTATUS WINAPI RtlDeleteAtomFromAtomTable( RTL_ATOM_TABLE table, RTL_ATOM atom )
82 {
83     NTSTATUS    status;
84
85     TRACE( "%p %x\n", table, atom );
86     if (!table) status = STATUS_INVALID_PARAMETER;
87     else
88     {
89         SERVER_START_REQ( delete_atom )
90         {
91             req->atom = atom;
92             req->table = wine_server_obj_handle( table );
93             status = wine_server_call( req );
94         }
95         SERVER_END_REQ;
96     }
97     return status;
98 }
99
100 /******************************************************************
101  *              integral_atom_name (internal)
102  *
103  * Helper for fetching integral (local/global) atoms names.
104  */
105 static ULONG integral_atom_name(WCHAR* buffer, ULONG len, RTL_ATOM atom)
106 {
107     static const WCHAR fmt[] = {'#','%','u',0};
108     WCHAR tmp[16];
109     int ret;
110
111     ret = sprintfW( tmp, fmt, atom );
112     if (!len) return ret * sizeof(WCHAR);
113     if (len <= ret) ret = len - 1;
114     memcpy( buffer, tmp, ret * sizeof(WCHAR) );
115     buffer[ret] = 0;
116     return ret * sizeof(WCHAR);
117 }
118
119 /******************************************************************
120  *              RtlQueryAtomInAtomTable (NTDLL.@)
121  */
122 NTSTATUS WINAPI RtlQueryAtomInAtomTable( RTL_ATOM_TABLE table, RTL_ATOM atom, ULONG* ref,
123                                          ULONG* pin, WCHAR* name, ULONG* len )
124 {
125     NTSTATUS    status = STATUS_SUCCESS;
126     DWORD       wlen = 0;
127
128     if (!table) status = STATUS_INVALID_PARAMETER;
129     else if (atom < MAXINTATOM)
130     {
131         if (!atom) return STATUS_INVALID_PARAMETER;
132         if (len) wlen = integral_atom_name( name, *len, atom);
133         if (ref) *ref = 1;
134         if (pin) *pin = 1;
135     }
136     else
137     {
138         SERVER_START_REQ( get_atom_information )
139         {
140             req->atom = atom;
141             req->table = wine_server_obj_handle( table );
142             if (len && *len && name)
143                 wine_server_set_reply( req, name, *len );
144             status = wine_server_call( req );
145             if (status == STATUS_SUCCESS)
146             {
147                 wlen = reply->total;
148                 if (ref) *ref = reply->count;
149                 if (pin) *pin = reply->pinned;
150             }
151         }
152         SERVER_END_REQ;
153     }
154     if (status == STATUS_SUCCESS && len)
155     {
156         if (*len)
157         {
158             wlen = min( *len - sizeof(WCHAR), wlen );
159             if (name) name[wlen / sizeof(WCHAR)] = 0;
160         }
161         else status = STATUS_BUFFER_TOO_SMALL;
162         *len = wlen;
163     }
164
165     TRACE( "%p %x -> %s (%x)\n",
166            table, atom, len ? debugstr_wn(name, wlen / sizeof(WCHAR)) : NULL, status );
167     return status;
168 }
169
170 /******************************************************************
171  *              RtlCreateAtomTable (NTDLL.@)
172  */
173 NTSTATUS WINAPI RtlCreateAtomTable( ULONG size, RTL_ATOM_TABLE* table )
174 {
175     NTSTATUS    status;
176
177     if (*table)
178     {
179         if (size) status = STATUS_INVALID_PARAMETER;
180         else status = STATUS_SUCCESS;
181     }
182     else
183     {
184         SERVER_START_REQ( init_atom_table )
185         {
186             req->entries = size;
187             status = wine_server_call( req );
188             *table = wine_server_ptr_handle( reply->table );
189         }
190         SERVER_END_REQ;
191     }
192     return status;
193 }
194
195 /******************************************************************
196  *              RtlDestroyAtomTable (NTDLL.@)
197  */
198 NTSTATUS WINAPI RtlDestroyAtomTable( RTL_ATOM_TABLE table )
199 {
200     if (!table) return STATUS_INVALID_PARAMETER;
201     return NtClose( table );
202 }
203
204 /******************************************************************
205  *              RtlAddAtomToAtomTable (NTDLL.@)
206  */
207 NTSTATUS WINAPI RtlAddAtomToAtomTable( RTL_ATOM_TABLE table, const WCHAR* name, RTL_ATOM* atom )
208 {
209     NTSTATUS    status;
210
211     if (!table) status = STATUS_INVALID_PARAMETER;
212     else
213     {
214         size_t len = HIWORD(name) ? strlenW(name) : 0;
215         status = is_integral_atom( name, len, atom );
216         if (status == STATUS_MORE_ENTRIES)
217         {
218             SERVER_START_REQ( add_atom )
219             {
220                 wine_server_add_data( req, name, len * sizeof(WCHAR) );
221                 req->table = wine_server_obj_handle( table );
222                 status = wine_server_call( req );
223                 *atom = reply->atom;
224             }
225             SERVER_END_REQ;
226         }
227     }
228     TRACE( "%p %s -> %x\n",
229            table, debugstr_w(name), status == STATUS_SUCCESS ? *atom : 0 );
230
231     return status;
232 }
233
234 /******************************************************************
235  *              RtlLookupAtomInAtomTable (NTDLL.@)
236  */
237 NTSTATUS WINAPI RtlLookupAtomInAtomTable( RTL_ATOM_TABLE table, const WCHAR* name, RTL_ATOM* atom )
238 {
239     NTSTATUS    status;
240
241     if (!table) status = STATUS_INVALID_PARAMETER;
242     else
243     {
244         size_t len = HIWORD(name) ? strlenW(name) : 0;
245         status = is_integral_atom( name, len, atom );
246         if (status == STATUS_MORE_ENTRIES)
247         {
248             SERVER_START_REQ( find_atom )
249             {
250                 wine_server_add_data( req, name, len * sizeof(WCHAR) );
251                 req->table = wine_server_obj_handle( table );
252                 status = wine_server_call( req );
253                 *atom = reply->atom;
254             }
255             SERVER_END_REQ;
256         }
257     }
258     TRACE( "%p %s -> %x\n",
259            table, debugstr_w(name), status == STATUS_SUCCESS ? *atom : 0 );
260     return status;
261 }
262
263 /******************************************************************
264  *              RtlEmptyAtomTable (NTDLL.@)
265  */
266 NTSTATUS WINAPI RtlEmptyAtomTable( RTL_ATOM_TABLE table, BOOLEAN delete_pinned )
267 {
268     NTSTATUS    status;
269
270     if (!table) status = STATUS_INVALID_PARAMETER;
271     else
272     {
273         SERVER_START_REQ( empty_atom_table )
274         {
275             req->table = wine_server_obj_handle( table );
276             req->if_pinned = delete_pinned;
277             status = wine_server_call( req );
278         }
279         SERVER_END_REQ;
280     }
281     return status;
282 }
283
284 /******************************************************************
285  *              RtlPinAtomInAtomTable (NTDLL.@)
286  */
287 NTSTATUS WINAPI RtlPinAtomInAtomTable( RTL_ATOM_TABLE table, RTL_ATOM atom )
288 {
289     NTSTATUS status;
290
291     if (!table) return STATUS_INVALID_PARAMETER;
292     if (atom < MAXINTATOM) return STATUS_SUCCESS;
293
294     SERVER_START_REQ( set_atom_information )
295     {
296         req->table = wine_server_obj_handle( table );
297         req->atom = atom;
298         req->pinned = TRUE;
299         status = wine_server_call( req );
300     }
301     SERVER_END_REQ;
302
303     return status;
304 }
305
306 /*************************************************
307  *        Global handle table management
308  *************************************************/
309
310 /******************************************************************
311  *              NtAddAtom (NTDLL.@)
312  */
313 NTSTATUS WINAPI NtAddAtom( const WCHAR* name, ULONG length, RTL_ATOM* atom )
314 {
315     NTSTATUS    status;
316
317     status = is_integral_atom( name, length / sizeof(WCHAR), atom );
318     if (status == STATUS_MORE_ENTRIES)
319     {
320         SERVER_START_REQ( add_atom )
321         {
322             wine_server_add_data( req, name, length );
323             req->table = 0;
324             status = wine_server_call( req );
325             *atom = reply->atom;
326         }
327         SERVER_END_REQ;
328     }
329     TRACE( "%s -> %x\n",
330            debugstr_wn(name, length/sizeof(WCHAR)), status == STATUS_SUCCESS ? *atom : 0 );
331     return status;
332 }
333
334 /******************************************************************
335  *              NtDeleteAtom (NTDLL.@)
336  */
337 NTSTATUS WINAPI NtDeleteAtom(RTL_ATOM atom)
338 {
339     NTSTATUS    status;
340
341     SERVER_START_REQ( delete_atom )
342     {
343         req->atom = atom;
344         req->table = 0;
345         status = wine_server_call( req );
346     }
347     SERVER_END_REQ;
348     return status;
349 }
350
351 /******************************************************************
352  *              NtFindAtom (NTDLL.@)
353  */
354 NTSTATUS WINAPI NtFindAtom( const WCHAR* name, ULONG length, RTL_ATOM* atom )
355 {
356     NTSTATUS    status;
357
358     status = is_integral_atom( name, length / sizeof(WCHAR), atom );
359     if (status == STATUS_MORE_ENTRIES)
360     {
361         SERVER_START_REQ( find_atom )
362         {
363             wine_server_add_data( req, name, length );
364             req->table = 0;
365             status = wine_server_call( req );
366             *atom = reply->atom;
367         }
368         SERVER_END_REQ;
369     }
370     TRACE( "%s -> %x\n",
371            debugstr_wn(name, length/sizeof(WCHAR)), status == STATUS_SUCCESS ? *atom : 0 );
372     return status;
373 }
374
375 /******************************************************************
376  *              NtQueryInformationAtom (NTDLL.@)
377  */
378 NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS class,
379                                         PVOID ptr, ULONG size, PULONG psize )
380 {
381     NTSTATUS status;
382
383     switch (class)
384     {
385     case AtomBasicInformation:
386         {
387             ULONG name_len;
388             ATOM_BASIC_INFORMATION* abi = ptr;
389
390             if (size < sizeof(ATOM_BASIC_INFORMATION))
391                 return STATUS_INVALID_PARAMETER;
392             name_len = size - sizeof(ATOM_BASIC_INFORMATION);
393
394             if (atom < MAXINTATOM)
395             {
396                 if (atom)
397                 {
398                     abi->NameLength = integral_atom_name( abi->Name, name_len, atom );
399                     status = (name_len) ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
400                     abi->ReferenceCount = 1;
401                     abi->Pinned = 1;
402                 }
403                 else status = STATUS_INVALID_PARAMETER;
404             }
405             else
406             {
407                 SERVER_START_REQ( get_atom_information )
408                 {
409                     req->atom = atom;
410                     req->table = 0;
411                     if (name_len) wine_server_set_reply( req, abi->Name, name_len );
412                     status = wine_server_call( req );
413                     if (status == STATUS_SUCCESS)
414                     {
415                         name_len = wine_server_reply_size( reply );
416                         if (name_len)
417                         {
418                             abi->NameLength = name_len;
419                             abi->Name[name_len / sizeof(WCHAR)] = '\0';
420                         }
421                         else
422                         {
423                             name_len = reply->total;
424                             abi->NameLength = name_len;
425                             status = STATUS_BUFFER_TOO_SMALL;
426                         }
427                         abi->ReferenceCount = reply->count;
428                         abi->Pinned = reply->pinned;
429                     }
430                     else name_len = 0;
431                 }
432                 SERVER_END_REQ;
433             }
434             TRACE( "%x -> %s (%u)\n", 
435                    atom, debugstr_wn(abi->Name, abi->NameLength / sizeof(WCHAR)),
436                    status );
437             if (psize)
438                 *psize = sizeof(ATOM_BASIC_INFORMATION) + name_len;
439         }
440         break;
441     default:
442         FIXME( "Unsupported class %u\n", class );
443         status = STATUS_INVALID_INFO_CLASS;
444         break;
445     }
446     return status;
447 }