Window style updates.
[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 "wine/winbase16.h"
20 #include "wine/winuser16.h"
21 #include "winuser.h"
22 #include "global.h"
23 #include "instance.h"
24 #include "ldt.h"
25 #include "stackframe.h"
26 #include "user.h"
27 #include "debugtools.h"
28
29 #ifdef CONFIG_IPC
30 #include "dde_atom.h"
31 #endif
32
33 DEFAULT_DEBUG_CHANNEL(atom)
34
35 #define DEFAULT_ATOMTABLE_SIZE    37
36 #define MIN_STR_ATOM              0xc000
37 #define MAX_ATOM_LEN              255
38
39 #define ATOMTOHANDLE(atom)        ((HANDLE16)(atom) << 2)
40 #define HANDLETOATOM(handle)      ((ATOM)(0xc000 | ((handle) >> 2)))
41
42 #define HAS_ATOM_TABLE(sel)  \
43           ((INSTANCEDATA*)PTR_SEG_OFF_TO_LIN(sel,0))->atomtable != 0)
44
45 #define GET_ATOM_TABLE(sel)  ((ATOMTABLE*)PTR_SEG_OFF_TO_LIN(sel, \
46           ((INSTANCEDATA*)PTR_SEG_OFF_TO_LIN(sel,0))->atomtable))
47
48 typedef struct
49 {
50     HANDLE16    next;
51     WORD        refCount;
52     BYTE        length;
53     BYTE        str[1];
54 } ATOMENTRY;
55
56 typedef struct
57 {
58     WORD        size;
59     HANDLE16    entries[1];
60 } ATOMTABLE;
61                 
62 static WORD ATOM_GlobalTable = 0;
63
64 /***********************************************************************
65  *           ATOM_InitTable
66  *
67  * NOTES
68  *      Should this validate the value of entries to be 0 < x < 0x3fff?
69  *
70  * RETURNS
71  *      Handle: Success
72  *      0: Failure
73  */
74 static HANDLE16 ATOM_InitTable(
75                 WORD selector, /* [in] Segment */
76                 WORD entries   /* [in] Size of atom table */
77 ) {
78     int i;
79     HANDLE16 handle;
80     ATOMTABLE *table;
81
82       /* We consider the first table to be initialized as the global table. 
83        * This works, as USER (both built-in and native) is the first one to 
84        * register ... 
85        */
86
87     if (!ATOM_GlobalTable) ATOM_GlobalTable = selector; 
88
89
90       /* Allocate the table */
91
92     handle = LOCAL_Alloc( selector, LMEM_FIXED,
93                           sizeof(ATOMTABLE) + (entries-1) * sizeof(HANDLE16) );
94     if (!handle) return 0;
95     table = (ATOMTABLE *)PTR_SEG_OFF_TO_LIN( selector, handle );
96     table->size = entries;
97     for (i = 0; i < entries; i++) table->entries[i] = 0;
98
99       /* Store a pointer to the table in the instance data */
100
101     ((INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 ))->atomtable = handle;
102     return handle;
103 }
104
105
106 /***********************************************************************
107  *           ATOM_Init
108  *
109  * Global table initialisation.
110  */
111 BOOL ATOM_Init( WORD globalTableSel )
112 {
113     return ATOM_InitTable( globalTableSel, DEFAULT_ATOMTABLE_SIZE ) != 0;
114 }
115
116
117 /***********************************************************************
118  *           ATOM_GetTable
119  *
120  * Return a pointer to the atom table of a given segment, creating
121  * it if necessary.
122  *
123  * RETURNS
124  *      Pointer to table: Success
125  *      NULL: Failure
126  */
127 static ATOMTABLE *ATOM_GetTable(
128                   WORD selector, /* [in] Segment */
129                   BOOL create  /* [in] Create */ )
130 {
131     INSTANCEDATA *ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 );
132     if (ptr->atomtable)
133     {
134         ATOMTABLE *table = (ATOMTABLE *)((char *)ptr + ptr->atomtable);
135         if (table->size) return table;
136     }
137     if (!create) return NULL;
138     if (!ATOM_InitTable( selector, DEFAULT_ATOMTABLE_SIZE )) return NULL;
139     /* Reload ptr in case it moved in linear memory */
140     ptr = (INSTANCEDATA *)PTR_SEG_OFF_TO_LIN( selector, 0 );
141     return (ATOMTABLE *)((char *)ptr + ptr->atomtable);
142 }
143
144
145 /***********************************************************************
146  *           ATOM_MakePtr
147  *
148  * Make an ATOMENTRY pointer from a handle (obtained from GetAtomHandle()).
149  */
150 static ATOMENTRY *ATOM_MakePtr(
151                   WORD selector,  /* [in] Segment */
152                   HANDLE16 handle /* [in] Handle */
153 ) {
154     return (ATOMENTRY *)PTR_SEG_OFF_TO_LIN( selector, handle );
155 }
156
157
158 /***********************************************************************
159  *           ATOM_Hash
160  * RETURNS
161  *      The hash value for the input string
162  */
163 static WORD ATOM_Hash(
164             WORD entries, /* [in] Total number of entries */
165             LPCSTR str,   /* [in] Pointer to string to hash */
166             WORD len      /* [in] Length of string */
167 ) {
168     WORD i, hash = 0;
169
170     TRACE("%x, %s, %x\n", entries, str, len);
171     
172     for (i = 0; i < len; i++) hash ^= toupper(str[i]) + i;
173     return hash % entries;
174 }
175
176 static BOOL ATOM_IsIntAtom(LPCSTR atomstr,WORD *atomid) {
177         LPSTR   xend;
178
179         if (!HIWORD(atomstr)) {
180                 *atomid = LOWORD(atomstr);
181                 return TRUE;
182         }
183         if (atomstr[0]!='#')
184                 return FALSE;
185         *atomid=strtol(atomstr+1,&xend,10);
186         if (*xend) {
187                 FIXME("found atom named '%s'\n",atomstr);
188                 return FALSE;
189         }
190         return TRUE;
191 }
192
193
194 /***********************************************************************
195  *           ATOM_AddAtom
196  *
197  * Windows DWORD aligns the atom entry size.
198  * The remaining unused string space created by the alignment
199  * gets padded with '\0's in a certain way to ensure
200  * that at least one trailing '\0' remains.
201  *
202  * RETURNS
203  *      Atom: Success
204  *      0: Failure
205  */
206 static ATOM ATOM_AddAtom(
207             WORD selector, /* [in] Segment */
208             LPCSTR str     /* [in] Pointer to the string to add */
209 ) {
210     WORD hash;
211     HANDLE16 entry;
212     ATOMENTRY * entryPtr;
213     ATOMTABLE * table;
214     int len, ae_len;
215     WORD        iatom;
216
217     TRACE("0x%x, %s\n", selector, str);
218     
219     if (ATOM_IsIntAtom(str,&iatom))
220         return iatom;
221     if ((len = strlen( str )) > MAX_ATOM_LEN) len = MAX_ATOM_LEN;
222     if (!(table = ATOM_GetTable( selector, TRUE ))) return 0;
223     hash = ATOM_Hash( table->size, str, len );
224     entry = table->entries[hash];
225     while (entry)
226     {
227         entryPtr = ATOM_MakePtr( selector, entry );
228         if ((entryPtr->length == len) && 
229             (!lstrncmpiA( entryPtr->str, str, len )))
230         {
231             entryPtr->refCount++;
232         TRACE("-- existing 0x%x\n", entry);
233             return HANDLETOATOM( entry );
234         }
235         entry = entryPtr->next;
236     }
237
238     ae_len = (sizeof(ATOMENTRY)+len+3) & ~3;
239     entry = LOCAL_Alloc( selector, LMEM_FIXED, ae_len);
240     if (!entry) return 0;
241     /* Reload the table ptr in case it moved in linear memory */
242     table = ATOM_GetTable( selector, FALSE );
243     entryPtr = ATOM_MakePtr( selector, entry );
244     entryPtr->next = table->entries[hash];
245     entryPtr->refCount = 1;
246     entryPtr->length = len;
247     strncpy( entryPtr->str, str, ae_len - sizeof(ATOMENTRY) + 1); /* always use strncpy ('\0's padding) */
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 #ifdef CONFIG_IPC
549     return DDE_GlobalAddAtom( str );
550 #else
551     return ATOM_AddAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) );
552 #endif
553 }
554
555
556 /***********************************************************************
557  *           GlobalAddAtom32A   (KERNEL32.313)
558  * Adds a character string to the global atom table and returns a unique
559  * value identifying the string.
560  *
561  * RETURNS
562  *      Atom: Success
563  *      0: Failure
564  */
565 ATOM WINAPI GlobalAddAtomA(
566             LPCSTR str /* [in] Pointer to string to add */
567 ) {
568     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
569     return ATOM_AddAtom( ATOM_GlobalTable, str );
570 }
571
572
573 /***********************************************************************
574  *           GlobalAddAtom32W   (KERNEL32.314)
575  * See GlobalAddAtom32A
576  */
577 ATOM WINAPI GlobalAddAtomW( LPCWSTR str )
578 {
579     char buffer[MAX_ATOM_LEN+1];
580     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
581     lstrcpynWtoA( buffer, str, sizeof(buffer) );
582     return ATOM_AddAtom( ATOM_GlobalTable, buffer );
583 }
584
585
586 /***********************************************************************
587  *           GlobalDeleteAtom   (USER.269) (KERNEL32.317)
588  * Decrements the reference count of a string atom.  If the count is
589  * zero, the string associated with the atom is removed from the table.
590  *
591  * RETURNS
592  *      0: Success
593  *      Atom: Failure
594  */
595 ATOM WINAPI GlobalDeleteAtom(
596             ATOM atom /* [in] Atom to delete */
597 ) {
598 #ifdef CONFIG_IPC
599     return DDE_GlobalDeleteAtom( atom );
600 #else
601     return ATOM_DeleteAtom( ATOM_GlobalTable, atom );
602 #endif
603 }
604
605
606 /***********************************************************************
607  *           GlobalFindAtom16   (USER.270)
608  */
609 ATOM WINAPI GlobalFindAtom16( SEGPTR str )
610 {
611     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
612 #ifdef CONFIG_IPC
613     return DDE_GlobalFindAtom( str );
614 #else
615     return ATOM_FindAtom( ATOM_GlobalTable, (LPCSTR)PTR_SEG_TO_LIN(str) );
616 #endif
617 }
618
619
620 /***********************************************************************
621  *           GlobalFindAtom32A   (KERNEL32.318)
622  * Searches the atom table for the string and returns the atom
623  * associated with it.
624  *
625  * RETURNS
626  *      Atom: Success
627  *      0: Failure
628  */
629 ATOM WINAPI GlobalFindAtomA(
630             LPCSTR str /* [in] Pointer to string to search for */
631 ) {
632     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
633     return ATOM_FindAtom( ATOM_GlobalTable, str );
634 }
635
636
637 /***********************************************************************
638  *           GlobalFindAtom32W   (KERNEL32.319)
639  * See GlobalFindAtom32A
640  */
641 ATOM WINAPI GlobalFindAtomW( LPCWSTR str )
642 {
643     char buffer[MAX_ATOM_LEN+1];
644     if (!HIWORD(str)) return (ATOM)LOWORD(str);  /* Integer atom */
645     lstrcpynWtoA( buffer, str, sizeof(buffer) );
646     return ATOM_FindAtom( ATOM_GlobalTable, buffer );
647 }
648
649
650 /***********************************************************************
651  *           GlobalGetAtomName16   (USER.271)
652  */
653 UINT16 WINAPI GlobalGetAtomName16( ATOM atom, LPSTR buffer, INT16 count )
654 {
655 #ifdef CONFIG_IPC
656     return DDE_GlobalGetAtomName( atom, buffer, count );
657 #else
658     return (UINT16)ATOM_GetAtomName( ATOM_GlobalTable, atom, buffer, count );
659 #endif
660 }
661
662
663 /***********************************************************************
664  *           GlobalGetAtomName32A   (KERNEL32.323)
665  * Retrieves a copy of the string associated with an atom.
666  *
667  * RETURNS
668  *      Length of string in characters: Success
669  *      0: Failure
670  */
671 UINT WINAPI GlobalGetAtomNameA(
672               ATOM atom,    /* [in]  Atom identifier */
673               LPSTR buffer, /* [out] Pointer to buffer for atom string */
674               INT count   /* [in]  Size of buffer */
675 ) {
676     return ATOM_GetAtomName( ATOM_GlobalTable, atom, buffer, count );
677 }
678
679
680 /***********************************************************************
681  *           GlobalGetAtomName32W   (KERNEL32.324)
682  * See GlobalGetAtomName32A
683  */
684 UINT WINAPI GlobalGetAtomNameW( ATOM atom, LPWSTR buffer, INT count )
685 {
686     char tmp[MAX_ATOM_LEN+1];
687     ATOM_GetAtomName( ATOM_GlobalTable, atom, tmp, sizeof(tmp) );
688     lstrcpynAtoW( buffer, tmp, count );
689     return lstrlenW( buffer );
690 }