Check, Radio & 3State buttons send WM_CTLCOLORSTATIC instead of
[wine] / server / atom.c
1 /*
2  * Server-side atom management
3  *
4  * Copyright (C) 1999, 2000 Alexandre Julliard
5  * Copyright (C) 2000 Turchanov Sergei
6  */
7
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #include "unicode.h"
14 #include "request.h"
15 #include "object.h"
16 #include "process.h"
17
18 #define HASH_SIZE     37
19 #define MIN_HASH_SIZE 4
20 #define MAX_HASH_SIZE 0x200
21
22 #define MAX_ATOM_LEN  255
23 #define MIN_STR_ATOM  0xc000
24 #define MAX_ATOMS     0x4000
25
26 struct atom_entry
27 {
28     struct atom_entry *next;   /* hash table list */
29     struct atom_entry *prev;   /* hash table list */
30     int                count;  /* reference count */
31     int                hash;   /* string hash */
32     atom_t             atom;   /* atom handle */
33     WCHAR              str[1]; /* atom string */
34 };
35
36 struct atom_table
37 {
38     struct object       obj;                 /* object header */
39     int                 count;               /* count of atom handles */
40     int                 last;                /* last handle in-use */
41     struct atom_entry **handles;             /* atom handles */
42     int                 entries_count;       /* humber of hash entries */
43     struct atom_entry **entries;             /* hash table entries */
44 };
45
46 static void atom_table_dump( struct object *obj, int verbose );
47 static void atom_table_destroy( struct object *obj );
48
49 static const struct object_ops atom_table_ops =
50 {
51     sizeof(struct atom_table),    /* size */
52     atom_table_dump,              /* dump */
53     no_add_queue,                 /* add_queue */
54     NULL,                         /* remove_queue */
55     NULL,                         /* signaled */
56     NULL,                         /* satified */
57     NULL,                         /* get_poll_events */
58     NULL,                         /* poll_event */
59     no_get_fd,                    /* get_fd */
60     no_flush,                     /* flush */
61     no_get_file_info,             /* get_file_info */
62     NULL,                         /* queue_async */
63     atom_table_destroy            /* destroy */
64 };
65
66 static struct atom_table *global_table;
67
68
69 /* copy an atom name from the request to a temporary area */
70 static const WCHAR *copy_request_name(void)
71 {
72     static WCHAR buffer[MAX_ATOM_LEN+1];
73
74     const WCHAR *str = get_req_data();
75     size_t len = get_req_data_size();
76
77     if (len > MAX_ATOM_LEN*sizeof(WCHAR))
78     {
79         set_error( STATUS_INVALID_PARAMETER );
80         return NULL;
81     }
82     memcpy( buffer, str, len );
83     buffer[len / sizeof(WCHAR)] = 0;
84     return buffer;
85 }
86
87 /* create an atom table */
88 static struct atom_table *create_table(int entries_count)
89 {
90     struct atom_table *table;
91
92     if ((table = alloc_object( &atom_table_ops, -1 )))
93     {
94         if ((entries_count < MIN_HASH_SIZE) ||
95             (entries_count > MAX_HASH_SIZE)) entries_count = HASH_SIZE;
96         table->entries_count = entries_count;
97         if (!(table->entries = malloc( sizeof(*table->entries) * table->entries_count )))
98         {
99             set_error( STATUS_NO_MEMORY );
100             goto fail;
101         }
102         memset( table->entries, 0, sizeof(*table->entries) * table->entries_count );
103         table->count = 64;
104         table->last  = -1;
105         if ((table->handles = mem_alloc( sizeof(*table->handles) * table->count )))
106             return table;
107 fail:
108         release_object( table );
109         table = NULL;
110     }
111     return table;
112 }
113
114 /* retrieve an entry pointer from its atom */
115 static struct atom_entry *get_atom_entry( struct atom_table *table, atom_t atom )
116 {
117     struct atom_entry *entry = NULL;
118     if (table && (atom >= MIN_STR_ATOM) && (atom <= MIN_STR_ATOM + table->last))
119         entry = table->handles[atom - MIN_STR_ATOM];
120     if (!entry) set_error( STATUS_INVALID_HANDLE );
121     return entry;
122 }
123
124 /* add an atom entry in the table and return its handle */
125 static atom_t add_atom_entry( struct atom_table *table, struct atom_entry *entry )
126 {
127     int i;
128     for (i = 0; i <= table->last; i++)
129         if (!table->handles[i]) goto found;
130     if (i == table->count)
131     {
132         struct atom_entry **new_table = NULL;
133         int new_size = table->count + table->count / 2;
134         if (new_size > MAX_ATOMS) new_size = MAX_ATOMS;
135         if (new_size > table->count)
136             new_table = realloc( table->handles, sizeof(*table->handles) * new_size );
137         if (!new_table)
138         {
139             set_error( STATUS_NO_MEMORY );
140             return 0;
141         }
142         table->count = new_size;
143         table->handles = new_table;
144     }
145     table->last = i;
146  found:
147     table->handles[i] = entry;
148     entry->atom = i + MIN_STR_ATOM;
149     return entry->atom;
150 }
151
152 /* compute the hash code for a string */
153 static int atom_hash( struct atom_table *table, const WCHAR *str )
154 {
155     int i;
156     WCHAR hash = 0;
157     for (i = 0; str[i]; i++) hash ^= toupperW(str[i]) + i;
158     return hash % table->entries_count;
159 }
160
161 /* dump an atom table */
162 static void atom_table_dump( struct object *obj, int verbose )
163 {
164     int i;
165     struct atom_table *table = (struct atom_table *)obj;
166     assert( obj->ops == &atom_table_ops );
167
168     fprintf( stderr, "Atom table size=%d entries=%d\n",
169              table->last + 1, table->entries_count );
170     if (!verbose) return;
171     for (i = 0; i <= table->last; i++)
172     {
173         struct atom_entry *entry = table->handles[i];
174         if (!entry) continue;
175         fprintf( stderr, "  %04x: ref=%d hash=%d \"", entry->atom, entry->count, entry->hash );
176         dump_strW( entry->str, strlenW(entry->str), stderr, "\"\"");
177         fprintf( stderr, "\"\n" );
178     }
179 }
180
181 /* destroy the atom table */
182 static void atom_table_destroy( struct object *obj )
183 {
184     int i;
185     struct atom_table *table = (struct atom_table *)obj;
186     assert( obj->ops == &atom_table_ops );
187     if (table->handles)
188     {
189         for (i = 0; i <= table->last; i++) free( table->handles[i] );
190         free( table->handles );
191     }
192     if (table->entries) free( table->entries );
193 }
194
195 /* find an atom entry in its hash list */
196 static struct atom_entry *find_atom_entry( struct atom_table *table, const WCHAR *str, int hash )
197 {
198     struct atom_entry *entry = table->entries[hash];
199     while (entry)
200     {
201         if (!strcmpiW( entry->str, str )) break;
202         entry = entry->next;
203     }
204     return entry;
205 }
206
207 /* close the atom table; used on server exit */
208 void close_atom_table(void)
209 {
210     if (global_table) release_object( global_table );
211 }
212
213 /* add an atom to the table */
214 static atom_t add_atom( struct atom_table *table, const WCHAR *str )
215 {
216     struct atom_entry *entry;
217     int hash = atom_hash( table, str );
218     atom_t atom = 0;
219
220     if (!*str)
221     {
222         set_error( STATUS_OBJECT_NAME_INVALID );
223         return 0;
224     }
225     if ((entry = find_atom_entry( table, str, hash )))  /* exists already */
226     {
227         entry->count++;
228         return entry->atom;
229     }
230
231     if ((entry = mem_alloc( sizeof(*entry) + strlenW(str) * sizeof(WCHAR) )))
232     {
233         if ((atom = add_atom_entry( table, entry )))
234         {
235             entry->prev  = NULL;
236             if ((entry->next = table->entries[hash])) entry->next->prev = entry;
237             table->entries[hash] = entry;
238             entry->count = 1;
239             entry->hash  = hash;
240             strcpyW( entry->str, str );
241         }
242         else free( entry );
243     }
244     else set_error( STATUS_NO_MEMORY );
245     return atom;
246 }
247
248 /* delete an atom from the table */
249 static void delete_atom( struct atom_table *table, atom_t atom )
250 {
251     struct atom_entry *entry = get_atom_entry( table, atom );
252     if (entry && !--entry->count)
253     {
254         if (entry->next) entry->next->prev = entry->prev;
255         if (entry->prev) entry->prev->next = entry->next;
256         else table->entries[entry->hash] = entry->next;
257         table->handles[atom - MIN_STR_ATOM] = NULL;
258         free( entry );
259     }
260 }
261
262 /* find an atom in the table */
263 static atom_t find_atom( struct atom_table *table, const WCHAR *str )
264 {
265     struct atom_entry *entry;
266
267     if (table && ((entry = find_atom_entry( table, str, atom_hash(table, str) ))))
268         return entry->atom;
269     if (!*str) set_error( STATUS_OBJECT_NAME_INVALID );
270     else set_error( STATUS_OBJECT_NAME_NOT_FOUND );
271     return 0;
272 }
273
274 /* increment the ref count of a global atom; used for window properties */
275 int grab_global_atom( atom_t atom )
276 {
277     struct atom_entry *entry = get_atom_entry( global_table, atom );
278     if (entry) entry->count++;
279     return (entry != NULL);
280 }
281
282 /* decrement the ref count of a global atom; used for window properties */
283 void release_global_atom( atom_t atom )
284 {
285     delete_atom( global_table, atom );
286 }
287
288 /* add a global atom */
289 DECL_HANDLER(add_atom)
290 {
291     struct atom_table **table_ptr = req->local ? &current->process->atom_table : &global_table;
292
293     if (!*table_ptr) *table_ptr = create_table(0);
294     if (*table_ptr)
295     {
296         const WCHAR *name = copy_request_name();
297         if (name) reply->atom = add_atom( *table_ptr, name );
298     }
299 }
300
301 /* delete a global atom */
302 DECL_HANDLER(delete_atom)
303 {
304     delete_atom( req->local ? current->process->atom_table : global_table, req->atom );
305 }
306
307 /* find a global atom */
308 DECL_HANDLER(find_atom)
309 {
310     const WCHAR *name = copy_request_name();
311     if (name)
312         reply->atom = find_atom( req->local ? current->process->atom_table : global_table, name );
313 }
314
315 /* get global atom name */
316 DECL_HANDLER(get_atom_name)
317 {
318     struct atom_entry *entry;
319     size_t len = 0;
320
321     reply->count = -1;
322     if ((entry = get_atom_entry( req->local ? current->process->atom_table : global_table,
323                                  req->atom )))
324     {
325         reply->count = entry->count;
326         len = strlenW( entry->str ) * sizeof(WCHAR);
327         if (len <= get_reply_max_size()) set_reply_data( entry->str, len );
328         else set_error( STATUS_BUFFER_OVERFLOW );
329     }
330 }
331
332 /* init the process atom table */
333 DECL_HANDLER(init_atom_table)
334 {
335     if (!current->process->atom_table)
336         current->process->atom_table = create_table( req->entries );
337 }