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