Added/fixed some documentation reported by winapi_check.
[wine] / memory / atom.c
1 /*
2  * Atom table functions
3  *
4  * Copyright 1993, 1994, 1995 Alexandre Julliard
5  */
6
7 /*
8  * Warning: The code assumes that LocalAlloc() returns a block aligned
9  * on a 4-bytes boundary (because of the shifting done in
10  * HANDLETOATOM).  If this is not the case, the allocation code will
11  * have to be changed.
12  */
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 #include "windef.h"
20 #include "wingdi.h"
21 #include "winuser.h"
22 #include "wine/winbase16.h"
23 #include "wine/winuser16.h"
24 #include "winerror.h"
25 #include "instance.h"
26 #include "ldt.h"
27 #include "stackframe.h"
28 #include "user.h"
29 #include "debugtools.h"
30 #include "server.h"
31
32 DEFAULT_DEBUG_CHANNEL(atom);
33
34 #define DEFAULT_ATOMTABLE_SIZE    37
35 #define MIN_STR_ATOM              0xc000
36 #define MAX_ATOM_LEN              255
37
38 #define ATOMTOHANDLE(atom)        ((HANDLE16)(atom) << 2)
39 #define HANDLETOATOM(handle)      ((ATOM)(0xc000 | ((handle) >> 2)))
40
41 typedef struct
42 {
43     HANDLE16    next;
44     WORD        refCount;
45     BYTE        length;
46     BYTE        str[1];
47 } ATOMENTRY;
48
49 typedef struct
50 {
51     WORD        size;
52     HANDLE16    entries[1];
53 } ATOMTABLE;
54                 
55 static WORD ATOM_UserDS = 0;  /* USER data segment */
56
57 /***********************************************************************
58  *           ATOM_Init
59  *
60  * Global table initialisation.
61  */
62 BOOL ATOM_Init( WORD globalTableSel )
63 {
64     ATOM_UserDS = globalTableSel;
65     return TRUE;
66 }
67
68
69 /***********************************************************************
70  *           ATOM_GetTable
71  *
72  * Return a pointer to the atom table of a given segment, creating
73  * it if necessary.
74  *
75  * RETURNS
76  *      Pointer to table: Success
77  *      NULL: Failure
78  */
79 static ATOMTABLE *ATOM_GetTable( BOOL create  /* [in] Create */ )
80 {
81     INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
82     if (ptr->atomtable)
83     {
84         ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
85         if (table->size) return table;
86     }
87     if (!create) return NULL;
88     if (!InitAtomTable16( 0 )) return NULL;
89     /* Reload ptr in case it moved in linear memory */
90     ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 );
91     return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
92 }
93
94
95 /***********************************************************************
96  *           ATOM_Hash
97  * RETURNS
98  *      The hash value for the input string
99  */
100 static WORD ATOM_Hash(
101             WORD entries, /* [in] Total number of entries */
102             LPCSTR str,   /* [in] Pointer to string to hash */
103             WORD len      /* [in] Length of string */
104 ) {
105     WORD i, hash = 0;
106
107     TRACE("%x, %s, %x\n", entries, str, len);
108     
109     for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
110     return hash % entries;
111 }
112
113
114 /***********************************************************************
115  *           ATOM_IsIntAtomA
116  */
117 static BOOL ATOM_IsIntAtomA(LPCSTR atomstr,WORD *atomid)
118 {
119     UINT atom = 0;
120     if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
121     else
122     {
123         if (*atomstr++ != '#') return FALSE;
124         while (*atomstr >= '0' && *atomstr <= '9')
125         {
126             atom = atom * 10 + *atomstr - '0';
127             atomstr++;
128         }
129         if (*atomstr) return FALSE;
130     }
131     if (!atom || (atom >= MIN_STR_ATOM))
132     {
133         SetLastError( ERROR_INVALID_PARAMETER );
134         atom = 0;
135     }
136     *atomid = atom;
137     return TRUE;
138 }
139
140
141 /***********************************************************************
142  *           ATOM_IsIntAtomW
143  */
144 static BOOL ATOM_IsIntAtomW(LPCWSTR atomstr,WORD *atomid)
145 {
146     UINT atom = 0;
147     if (!HIWORD(atomstr)) atom = LOWORD(atomstr);
148     else
149     {
150         if (*atomstr++ != '#') return FALSE;
151         while (*atomstr >= '0' && *atomstr <= '9')
152         {
153             atom = atom * 10 + *atomstr - '0';
154             atomstr++;
155         }
156         if (*atomstr) return FALSE;
157     }
158     if (!atom || (atom >= MIN_STR_ATOM))
159     {
160         SetLastError( ERROR_INVALID_PARAMETER );
161         atom = 0;
162     }
163     *atomid = atom;
164     return TRUE;
165 }
166
167
168 /***********************************************************************
169  *           ATOM_MakePtr
170  *
171  * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
172  */
173 static inline ATOMENTRY *ATOM_MakePtr( HANDLE16 handle /* [in] Handle */ )
174 {
175     return (ATOMENTRY *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
176 }
177
178
179 /***********************************************************************
180  *           InitAtomTable16   (KERNEL.68)
181  */
182 WORD WINAPI InitAtomTable16( WORD entries )
183 {
184     int i;
185     HANDLE16 handle;
186     ATOMTABLE *table;
187
188       /* We consider the first table to be initialized as the global table. 
189        * This works, as USER (both built-in and native) is the first one to 
190        * register ... 
191        */
192
193     if (!ATOM_UserDS)
194     {
195         ATOM_UserDS = CURRENT_DS; 
196         /* return dummy local handle */
197         return LocalAlloc16( LMEM_FIXED, 1 );
198     }
199
200       /* Allocate the table */
201
202     if (!entries) entries = DEFAULT_ATOMTABLE_SIZE;  /* sanity check */
203     handle = LocalAlloc16( LMEM_FIXED, sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
204     if (!handle) return 0;
205     table = (ATOMTABLE *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, handle );
206     table->size = entries;
207     for (i = 0; i < entries; i++) table->entries[i] = 0;
208
209       /* Store a pointer to the table in the instance data */
210
211     ((INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( CURRENT_DS, 0 ))->atomtable = handle;
212     return handle;
213 }
214
215
216 /***********************************************************************
217  *           GetAtomHandle   (KERNEL.73)
218  */
219 HANDLE16 WINAPI GetAtomHandle16( ATOM atom )
220 {
221     if (atom < MIN_STR_ATOM) return 0;
222     return ATOMTOHANDLE( atom );
223 }
224
225
226 /***********************************************************************
227  *           AddAtom16   (KERNEL.70)
228  *
229  * Windows DWORD aligns the atom entry size.
230  * The remaining unused string space created by the alignment
231  * gets padded with '\0's in a certain way to ensure
232  * that at least one trailing '\0' remains.
233  *
234  * RETURNS
235  *      Atom: Success
236  *      0: Failure
237  */
238 ATOM WINAPI AddAtom16( LPCSTR str )
239 {
240     char buffer[MAX_ATOM_LEN+1];
241     WORD hash;
242     HANDLE16 entry;
243     ATOMENTRY * entryPtr;
244     ATOMTABLE * table;
245     int len, ae_len;
246     WORD        iatom;
247
248     if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
249
250     TRACE("%s\n",debugstr_a(buffer));
251     
252     /* Make a copy of the string to be sure it doesn't move in linear memory. */
253     lstrcpynA( buffer, str, sizeof(buffer) );
254
255     len = strlen( buffer );
256     if (!(table = ATOM_GetTable( TRUE ))) return 0;
257     if (CURRENT_DS == ATOM_UserDS) return GlobalAddAtomA( str );
258
259     hash = ATOM_Hash( table->size, buffer, len );
260     entry = table->entries[hash];
261     while (entry)
262     {
263         entryPtr = ATOM_MakePtr( entry );
264         if ((entryPtr->length == len) && 
265             (!lstrncmpiA( entryPtr->str, buffer, len )))
266         {
267             entryPtr->refCount++;
268             TRACE("-- existing 0x%x\n", entry);
269             return HANDLETOATOM( entry );
270         }
271         entry = entryPtr->next;
272     }
273
274     ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
275     entry = LocalAlloc16( LMEM_FIXED, ae_len );
276     if (!entry) return 0;
277     /* Reload the table ptr in case it moved in linear memory */
278     table = ATOM_GetTable( FALSE );
279     entryPtr = ATOM_MakePtr( entry );
280     entryPtr->next = table->entries[hash];
281     entryPtr->refCount = 1;
282     entryPtr->length = len;
283     /* Some applications _need_ the '\0' padding provided by this strncpy */
284     strncpy( entryPtr->str, buffer, ae_len - sizeof(ATOMENTRY) + 1 );
285     entryPtr->str[ae_len - sizeof(ATOMENTRY)] = '\0';
286     table->entries[hash] = entry;
287     TRACE("-- new 0x%x\n", entry);
288     return HANDLETOATOM( entry );
289 }
290
291
292 /***********************************************************************
293  *           DeleteAtom16   (KERNEL.71)
294  */
295 ATOM WINAPI DeleteAtom16( ATOM atom )
296 {
297     ATOMENTRY * entryPtr;
298     ATOMTABLE * table;
299     HANDLE16 entry, *prevEntry;
300     WORD hash;
301
302     if (atom < MIN_STR_ATOM) return 0;  /* Integer atom */
303     if (CURRENT_DS == ATOM_UserDS) return GlobalDeleteAtom( atom );
304
305     TRACE("0x%x\n",atom);
306
307     if (!(table = ATOM_GetTable( FALSE ))) return 0;
308     entry = ATOMTOHANDLE( atom );
309     entryPtr = ATOM_MakePtr( entry );
310
311     /* Find previous atom */
312     hash = ATOM_Hash( table->size, entryPtr->str, entryPtr->length );
313     prevEntry = &table->entries[hash];
314     while (*prevEntry && *prevEntry != entry)
315     {
316         ATOMENTRY * prevEntryPtr = ATOM_MakePtr( *prevEntry );
317         prevEntry = &prevEntryPtr->next;
318     }    
319     if (!*prevEntry) return atom;
320
321     /* Delete atom */
322     if (--entryPtr->refCount == 0)
323     {
324         *prevEntry = entryPtr->next;
325         LocalFree16( entry );
326     }    
327     return 0;
328 }
329
330
331 /***********************************************************************
332  *           FindAtom16   (KERNEL.69)
333  */
334 ATOM WINAPI FindAtom16( LPCSTR str )
335 {
336     ATOMTABLE * table;
337     WORD hash,iatom;
338     HANDLE16 entry;
339     int len;
340
341     if (CURRENT_DS == ATOM_UserDS) return GlobalFindAtomA( str );
342
343     TRACE("%s\n",debugres_a(str));
344
345     if (ATOM_IsIntAtomA( str, &iatom )) return iatom;
346     if ((len = strlen( str )) > 255) len = 255;
347     if (!(table = ATOM_GetTable( FALSE ))) return 0;
348     hash = ATOM_Hash( table->size, str, len );
349     entry = table->entries[hash];
350     while (entry)
351     {
352         ATOMENTRY * entryPtr = ATOM_MakePtr( entry );
353         if ((entryPtr->length == len) && 
354             (!lstrncmpiA( entryPtr->str, str, len )))
355         {
356             TRACE("-- found %x\n", entry);
357             return HANDLETOATOM( entry );
358         }
359         entry = entryPtr->next;
360     }
361     TRACE("-- not found\n");
362     return 0;
363 }
364
365
366 /***********************************************************************
367  *           GetAtomName16   (KERNEL.72)
368  */
369 UINT16 WINAPI GetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
370 {
371     ATOMTABLE * table;
372     ATOMENTRY * entryPtr;
373     HANDLE16 entry;
374     char * strPtr;
375     UINT len;
376     char text[8];
377
378     if (CURRENT_DS == ATOM_UserDS) return GlobalGetAtomNameA( atom, buffer, count );
379     
380     TRACE("%x\n",atom);
381     
382     if (!count) return 0;
383     if (atom < MIN_STR_ATOM)
384     {
385         sprintf( text, "#%d", atom );
386         len = strlen(text);
387         strPtr = text;
388     }
389     else
390     {
391        if (!(table = ATOM_GetTable( FALSE ))) return 0;
392         entry = ATOMTOHANDLE( atom );
393         entryPtr = ATOM_MakePtr( entry );
394         len = entryPtr->length;
395         strPtr = entryPtr->str;
396     }
397     if (len >= count) len = count-1;
398     memcpy( buffer, strPtr, len );
399     buffer[len] = '\0';
400     return len;
401 }
402
403
404 /***********************************************************************
405  *           AddAtomA   (KERNEL32.0)
406  * Adds a string to the atom table and returns the atom identifying the
407  * string.
408  *
409  * RETURNS
410  *      Atom: Success
411  *      0: Failure
412  */
413 ATOM WINAPI AddAtomA(
414             LPCSTR str /* [in] Pointer to string to add */
415 ) {
416     return GlobalAddAtomA( str );  /* FIXME */
417 }
418
419
420 /***********************************************************************
421  *           AddAtomW   (KERNEL32.1)
422  * See AddAtomA
423  */
424 ATOM WINAPI AddAtomW( LPCWSTR str )
425 {
426     return GlobalAddAtomW( str );  /* FIXME */
427 }
428
429
430 /***********************************************************************
431  *           DeleteAtom   (KERNEL32.69)
432  * Decrements the reference count of a string atom.  If count becomes
433  * zero, the string associated with the atom is removed from the table.
434  *
435  * RETURNS
436  *      0: Success
437  *      Atom: Failure
438  */
439 ATOM WINAPI DeleteAtom(
440             ATOM atom /* [in] Atom to delete */
441 ) {
442     return GlobalDeleteAtom( atom );  /* FIXME */
443 }
444
445
446 /***********************************************************************
447  *           FindAtomA   (KERNEL32.117)
448  * Searches the local atom table for the string and returns the atom
449  * associated with that string.
450  *
451  * RETURNS
452  *      Atom: Success
453  *      0: Failure
454  */
455 ATOM WINAPI FindAtomA(
456             LPCSTR str /* [in] Pointer to string to find */
457 ) {
458     return GlobalFindAtomA( str );  /* FIXME */
459 }
460
461
462 /***********************************************************************
463  *           FindAtomW   (KERNEL32.118)
464  * See FindAtomA
465  */
466 ATOM WINAPI FindAtomW( LPCWSTR str )
467 {
468     return GlobalFindAtomW( str );  /* FIXME */
469 }
470
471
472 /***********************************************************************
473  *           GetAtomNameA   (KERNEL32.149)
474  * Retrieves a copy of the string associated with the atom.
475  *
476  * RETURNS
477  *      Length of string: Success
478  *      0: Failure
479  */
480 UINT WINAPI GetAtomNameA(
481               ATOM atom,    /* [in]  Atom */
482               LPSTR buffer, /* [out] Pointer to string for atom string */
483               INT count   /* [in]  Size of buffer */
484 ) {
485     return GlobalGetAtomNameA( atom, buffer, count );  /* FIXME */
486 }
487
488
489 /***********************************************************************
490  *           GetAtomNameW   (KERNEL32.150)
491  * See GetAtomNameA
492  */
493 UINT WINAPI GetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
494 {
495     return GlobalGetAtomNameW( atom, buffer, count );  /* FIXME */
496 }
497
498
499 /***********************************************************************
500  *           GlobalAddAtomA   (USER.268) (KERNEL32.313)
501  *
502  * Adds a character string to the global atom table and returns a unique
503  * value identifying the string.
504  *
505  * RETURNS
506  *      Atom: Success
507  *      0: Failure
508  */
509 ATOM WINAPI GlobalAddAtomA( LPCSTR str /* [in] Pointer to string to add */ )
510 {
511     ATOM atom = 0;
512     if (!ATOM_IsIntAtomA( str, &atom ))
513     {
514         struct add_atom_request *req = get_req_buffer();
515         server_strcpyAtoW( req->name, str );
516         if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
517     }
518     TRACE( "%s -> %x\n", debugres_a(str), atom );
519     return atom;
520 }
521
522
523 /***********************************************************************
524  *           GlobalAddAtomW   (KERNEL32.314)
525  */
526 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
527 {
528     ATOM atom = 0;
529     if (!ATOM_IsIntAtomW( str, &atom ))
530     {
531         struct add_atom_request *req = get_req_buffer();
532         server_strcpyW( req->name, str );
533         if (!server_call( REQ_ADD_ATOM )) atom = req->atom + MIN_STR_ATOM;
534     }
535     TRACE( "%s -> %x\n", debugres_w(str), atom );
536     return atom;
537 }
538
539
540 /***********************************************************************
541  *           GlobalDeleteAtom   (USER.269) (KERNEL32.317)
542  * Decrements the reference count of a string atom.  If the count is
543  * zero, the string associated with the atom is removed from the table.
544  *
545  * RETURNS
546  *      0: Success
547  *      Atom: Failure
548  */
549 ATOM WINAPI GlobalDeleteAtom( ATOM atom /* [in] Atom to delete */ )
550 {
551     TRACE( "%x\n", atom );
552     if (atom < MIN_STR_ATOM) atom = 0;
553     else
554     {
555         struct delete_atom_request *req = get_req_buffer();
556         req->atom = atom - MIN_STR_ATOM;
557         if (!server_call( REQ_DELETE_ATOM )) atom = 0;
558     }
559     return atom;
560 }
561
562
563 /***********************************************************************
564  *           GlobalFindAtomA   (USER.270) (KERNEL32.318)
565  *
566  * Searches the atom table for the string and returns the atom
567  * associated with it.
568  *
569  * RETURNS
570  *      Atom: Success
571  *      0: Failure
572  */
573 ATOM WINAPI GlobalFindAtomA( LPCSTR str /* [in] Pointer to string to search for */ )
574 {
575     ATOM atom = 0;
576     if (!ATOM_IsIntAtomA( str, &atom ))
577     {
578         struct find_atom_request *req = get_req_buffer();
579         server_strcpyAtoW( req->name, str );
580         if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
581     }
582     TRACE( "%s -> %x\n", debugres_a(str), atom );
583     return atom;
584 }
585
586
587 /***********************************************************************
588  *           GlobalFindAtomW   (KERNEL32.319)
589  */
590 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
591 {
592     ATOM atom = 0;
593     if (!ATOM_IsIntAtomW( str, &atom ))
594     {
595         struct find_atom_request *req = get_req_buffer();
596         server_strcpyW( req->name, str );
597         if (!server_call( REQ_FIND_ATOM )) atom = req->atom + MIN_STR_ATOM;
598     }
599     TRACE( "%s -> %x\n", debugres_w(str), atom );
600     return atom;
601 }
602
603
604 /***********************************************************************
605  *           GlobalGetAtomNameA   (USER.271) (KERNEL32.323)
606  *
607  * Retrieves a copy of the string associated with an atom.
608  *
609  * RETURNS
610  *      Length of string in characters: Success
611  *      0: Failure
612  */
613 UINT WINAPI GlobalGetAtomNameA(
614               ATOM atom,    /* [in]  Atom identifier */
615               LPSTR buffer, /* [out] Pointer to buffer for atom string */
616               INT count )   /* [in]  Size of buffer */
617 {
618     INT len;
619     if (atom < MIN_STR_ATOM)
620     {
621         char name[8];
622         if (!atom)
623         {
624             SetLastError( ERROR_INVALID_PARAMETER );
625             return 0;
626         }
627         len = sprintf( name, "#%d", atom );
628         lstrcpynA( buffer, name, count );
629     }
630     else
631     {
632         struct get_atom_name_request *req = get_req_buffer();
633         req->atom = atom - MIN_STR_ATOM;
634         if (server_call( REQ_GET_ATOM_NAME )) return 0;
635         lstrcpynWtoA( buffer, req->name, count );
636         len = lstrlenW( req->name );
637     }
638     if (count <= len)
639     {
640         SetLastError( ERROR_MORE_DATA );
641         return 0;
642     }
643     TRACE( "%x -> %s\n", atom, debugstr_a(buffer) );
644     return len;
645 }
646
647
648 /***********************************************************************
649  *           GlobalGetAtomNameW   (KERNEL32.324)
650  */
651 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
652 {
653     INT len;
654     if (atom < MIN_STR_ATOM)
655     {
656         char name[8];
657         if (!atom)
658         {
659             SetLastError( ERROR_INVALID_PARAMETER );
660             return 0;
661         }
662         len = sprintf( name, "#%d", atom );
663         lstrcpynAtoW( buffer, name, count );
664     }
665     else
666     {
667         struct get_atom_name_request *req = get_req_buffer();
668         req->atom = atom - MIN_STR_ATOM;
669         if (server_call( REQ_GET_ATOM_NAME )) return 0;
670         lstrcpynW( buffer, req->name, count );
671         len = lstrlenW( req->name );
672     }
673     if (count <= len)
674     {
675         SetLastError( ERROR_MORE_DATA );
676         return 0;
677     }
678     TRACE( "%x -> %s\n", atom, debugstr_w(buffer) );
679     return len;
680 }