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